Skip to content

feat: add jwt validation action and docs#1348

Merged
fabian-hiller merged 16 commits intoopen-circle:mainfrom
yslpn:feat-jwt-action
Mar 17, 2026
Merged

feat: add jwt validation action and docs#1348
fabian-hiller merged 16 commits intoopen-circle:mainfrom
yslpn:feat-jwt-action

Conversation

@yslpn
Copy link
Copy Markdown
Contributor

@yslpn yslpn commented Nov 8, 2025

Overview

  • rename the proposed jwt() action to jwsCompact() and scope it to the 3-part JWS compact serialization
  • leave room for a future jweCompact() action for 5-part compact tokens and keep jwt() reserved for broader JWT support
  • add runtime and type tests, website docs, and @valibot/to-json-schema support for jwsCompact()
  • document that this is only a lightweight structural check; full JWT validation, signature verification, and claim checks should use dedicated libraries

Summary by CodeRabbit

  • New Features

    • Added a JWS Compact validation action to verify JWS compact-serialized strings (three unpadded Base64URL segments) and expose it in the public API.
  • Tests

    • Added runtime and type-level tests covering valid and invalid JWS compact inputs.
  • Documentation

    • Added API reference pages, property metadata, examples, and updated API menu entries for the JwsCompact action and related types.
  • Chores

    • Exported the new action for broader tooling and schema conversion support.

Copilot AI review requested due to automatic review settings November 8, 2025 22:19
@vercel
Copy link
Copy Markdown

vercel Bot commented Nov 8, 2025

@yslpn is attempting to deploy a commit to the Valibot Team on Vercel.

A member of the Team first needs to authorize it.

@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. documentation Improvements or additions to documentation enhancement New feature or request labels Nov 8, 2025
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds JWT (JSON Web Token) validation functionality to the library. It introduces a new validation action that verifies whether a string conforms to the three-part Base64URL-encoded structure of a JWT token.

  • Adds jwt() validation action with regex-based format checking
  • Implements JwtAction and JwtIssue types with appropriate generics
  • Includes comprehensive test coverage for valid/invalid JWT formats

Reviewed Changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
website/src/routes/api/menu.md Adds menu entries for jwt, JwtAction, and JwtIssue API documentation
website/src/routes/api/(types)/JwtIssue/properties.ts Defines property metadata for JwtIssue type documentation
website/src/routes/api/(types)/JwtIssue/index.mdx Documents the JwtIssue interface
website/src/routes/api/(types)/JwtAction/properties.ts Defines property metadata for JwtAction type documentation
website/src/routes/api/(types)/JwtAction/index.mdx Documents the JwtAction interface
website/src/routes/api/(actions)/jwt/properties.ts Defines property metadata for jwt function documentation
website/src/routes/api/(actions)/jwt/index.mdx Provides comprehensive documentation and examples for the jwt validation action
library/src/regex.ts Adds JWT_REGEX pattern for validating JWT token structure
library/src/actions/jwt/jwt.ts Implements the jwt validation action with JwtAction and JwtIssue interfaces
library/src/actions/jwt/jwt.test.ts Adds runtime tests for jwt validation with valid/invalid test cases
library/src/actions/jwt/jwt.test-d.ts Adds type-level tests for jwt validation action
library/src/actions/jwt/index.ts Exports jwt validation action
library/src/actions/index.ts Re-exports jwt from actions index

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread library/src/actions/jwt/jwt.ts Outdated
@fabian-hiller fabian-hiller self-assigned this Jan 6, 2026
@fabian-hiller fabian-hiller added this to the v1.3 milestone Jan 6, 2026
@fabian-hiller
Copy link
Copy Markdown
Member

Sorry for my late replay! Thank you for this PR! We really need this action! I will review this PR when working on Valibot v1.3.

@fabian-hiller
Copy link
Copy Markdown
Member

Are you already a member of the Valibot Discord?

@yslpn
Copy link
Copy Markdown
Contributor Author

yslpn commented Jan 6, 2026

Are you already a member of the Valibot Discord?

Yes. Same username as on GitHub.

We really need this action! I will review this PR when working on Valibot v1.3.

Agreed. I'll be here if any fixes are needed. Or if there are any questions.

@fabian-hiller fabian-hiller requested a review from Copilot March 7, 2026 08:55
@fabian-hiller
Copy link
Copy Markdown
Member

@coderabbitai please review this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 7, 2026

@fabian-hiller Sure! I'll start reviewing this PR now.

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new JWS compact validation action: types, runtime validator using a new regex, tests (runtime and type-level), JSON Schema conversion support, and full API/type documentation and navigation/menu entries.

Changes

Cohort / File(s) Summary
Core Action
library/src/actions/jwsCompact/jwsCompact.ts, library/src/actions/jwsCompact/index.ts, library/src/actions/index.ts
Adds JwsCompactAction/JwsCompactIssue types, overloaded jwsCompact() factory with runtime ~run using JWS_COMPACT_REGEX, and re-exports the action from actions barrels.
Regex
library/src/regex.ts
Introduces exported JWS_COMPACT_REGEX: RegExp for JWS compact serialization matching.
Tests
library/src/actions/jwsCompact/jwsCompact.test.ts, library/src/actions/jwsCompact/jwsCompact.test-d.ts
Adds runtime and type-level tests covering valid/invalid JWS compact strings and type inference assertions.
JSON Schema Conversion
packages/to-json-schema/src/converters/convertAction/convertAction.ts, packages/to-json-schema/src/converters/convertAction/convertAction.test.ts
Extends converter to handle jws_compact action, emitting a JSON Schema pattern using the regex source; adds tests for conversion output.
Website — Actions Docs
website/src/routes/api/(actions)/jwsCompact/index.mdx, website/src/routes/api/(actions)/jwsCompact/properties.ts
Adds documentation page and properties descriptors for the jwsCompact action, examples, and usage notes.
Website — Types Docs
website/src/routes/api/(types)/JwsCompactAction/index.mdx, website/src/routes/api/(types)/JwsCompactAction/properties.ts, website/src/routes/api/(types)/JwsCompactIssue/index.mdx, website/src/routes/api/(types)/JwsCompactIssue/properties.ts
Adds MDX documentation and property metadata for JwsCompactAction and JwsCompactIssue types.
Website — Navigation
website/src/routes/api/menu.md
Adds menu entries for JwsCompact schema/type and action endpoints in API navigation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I found a compact token to test,
Three parts, base64url at best,
A regex to check, types to enforce,
Tests and docs guide the new course,
Hoppy deploy — validation blessed!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add jwt validation action and docs' accurately reflects the primary changes: introducing a new JWS compact validation action (jwsCompact) and comprehensive documentation for it, including API docs, type definitions, tests, and schema support.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread website/src/routes/api/(actions)/jwt/index.mdx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@library/src/regex.ts`:
- Line 120: The current JWT_REGEX allows '=' padding and disallows unsecured
JWTs with an empty signature; update the JWT_REGEX constant so it matches the
standard compact JWT form: exactly three dot-separated Base64URL segments where
the first two segments require one or more Base64URL chars ([A-Za-z0-9_-]+) and
the third (signature) may be empty ([A-Za-z0-9_-]*), and do not permit '='
padding; replace the existing /^[-\w]+={0,2}\.[-\w]+={0,2}\.[-\w]+={0,2}$/u with
a regex that enforces those token characters for JWT_REGEX so jwt() (used at
jwt.ts:92-97) correctly classifies tokens.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dceccdc4-321e-4bf2-9875-163b16fb87ef

📥 Commits

Reviewing files that changed from the base of the PR and between 3833d69 and d34fb29.

📒 Files selected for processing (13)
  • library/src/actions/index.ts
  • library/src/actions/jwt/index.ts
  • library/src/actions/jwt/jwt.test-d.ts
  • library/src/actions/jwt/jwt.test.ts
  • library/src/actions/jwt/jwt.ts
  • library/src/regex.ts
  • website/src/routes/api/(actions)/jwt/index.mdx
  • website/src/routes/api/(actions)/jwt/properties.ts
  • website/src/routes/api/(types)/JwtAction/index.mdx
  • website/src/routes/api/(types)/JwtAction/properties.ts
  • website/src/routes/api/(types)/JwtIssue/index.mdx
  • website/src/routes/api/(types)/JwtIssue/properties.ts
  • website/src/routes/api/menu.md

Comment thread library/src/regex.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
library/src/actions/jwt/jwt.test.ts (1)

125-130: Use a real Unicode character in the last invalid-character case.

Line 129 currently contains the literal text \u00a9 because the backslash is escaped, so this only proves backslashes are rejected. If the goal is non-ASCII coverage, use © or \u00a9 directly in the string literal.

🧪 Tiny fixture fix
-        'header.payload.sig\\u00a9nature',
+        'header.payload.sig\u00a9nature',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@library/src/actions/jwt/jwt.test.ts` around lines 125 - 130, The test 'for
invalid characters' is using an escaped backslash sequence "\\u00a9" instead of
an actual Unicode character; update the third test string passed to
expectActionIssue (inside the test named 'for invalid characters') to include a
real non-ASCII character (e.g., "©" or the literal "\u00a9" without an extra
backslash) so the case exercises Unicode rejection rather than just backslash
escaping; keep the other two inputs and variables (action, baseIssue) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@library/src/actions/jwt/jwt.test.ts`:
- Around line 67-76: The test 'for valid JWTs' currently includes real-looking
JWT strings that trigger secret scanners; change the fixtures used in
expectNoActionIssue to synthetic base64url-safe segments instead (e.g., generate
header, payload, signature strings that match JWT_REGEX but are not real
secrets) so coverage of jwt() / JWT_REGEX remains the same; update the array
passed to expectNoActionIssue in the test (and any other similar arrays in this
file) to use these synthetic base64url-safe segment placeholders rather than raw
JWT literals.

---

Nitpick comments:
In `@library/src/actions/jwt/jwt.test.ts`:
- Around line 125-130: The test 'for invalid characters' is using an escaped
backslash sequence "\\u00a9" instead of an actual Unicode character; update the
third test string passed to expectActionIssue (inside the test named 'for
invalid characters') to include a real non-ASCII character (e.g., "©" or the
literal "\u00a9" without an extra backslash) so the case exercises Unicode
rejection rather than just backslash escaping; keep the other two inputs and
variables (action, baseIssue) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 57a06127-a2bc-41a9-9a7a-d1fb4f23f7e1

📥 Commits

Reviewing files that changed from the base of the PR and between d34fb29 and b29fb3d.

📒 Files selected for processing (3)
  • library/src/actions/jwt/jwt.test.ts
  • library/src/regex.ts
  • website/src/routes/api/(actions)/jwt/index.mdx
🚧 Files skipped from review as they are similar to previous changes (2)
  • website/src/routes/api/(actions)/jwt/index.mdx
  • library/src/regex.ts

Comment thread library/src/actions/jwt/jwt.test.ts Outdated
@fabian-hiller
Copy link
Copy Markdown
Member

Thank you for this PR. I am trying to get most important PRs merged in the next weeks. One thing I’m not sure about is whether regex is the right boundary for jwt(). I would love to discuss this with you.

The current implementation seems reasonable if the intention is just a lightweight structural check for the common 3-part JWT/JWS shape. In that sense, regex is simple, fast, and consistent with several other format validators in the library.

That said, it might be worth aligning on what jwt() is expected to mean:

  • If it is meant as a loose “looks like a JWT” check, the regex approach feels fine.
  • If it is meant as a stricter JWT-format validator, regex alone may be too narrow in some places and too permissive in others.
    • But if a strict check results in a huge bundle size it is probably also not perfect for Valibot

For example, this currently only allows the 3-part compact form, so it would exclude JWTs represented as JWE compact serialization with 5 parts. On the other hand, it will also accept strings that are just three base64url-like segments even if the decoded header/payload are not valid JSON.

I don’t see this as a blocker by itself, but I think it would be good to decide explicitly which contract we want the jwt() action to have, and then shape the implementation/docs around that.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Mar 13, 2026

Open in StackBlitz

npm i https://pkg.pr.new/valibot@1348

commit: 660ca1c

@yslpn
Copy link
Copy Markdown
Contributor Author

yslpn commented Mar 14, 2026

Thank you for this PR. I am trying to get most important PRs merged in the next weeks. One thing I’m not sure about is whether regex is the right boundary for jwt(). I would love to discuss this with you.

The current implementation seems reasonable if the intention is just a lightweight structural check for the common 3-part JWT/JWS shape. In that sense, regex is simple, fast, and consistent with several other format validators in the library.

That said, it might be worth aligning on what jwt() is expected to mean:

  • If it is meant as a loose “looks like a JWT” check, the regex approach feels fine.

  • If it is meant as a stricter JWT-format validator, regex alone may be too narrow in some places and too permissive in others.

    • But if a strict check results in a huge bundle size it is probably also not perfect for Valibot

For example, this currently only allows the 3-part compact form, so it would exclude JWTs represented as JWE compact serialization with 5 parts. On the other hand, it will also accept strings that are just three base64url-like segments even if the decoded header/payload are not valid JSON.

I don’t see this as a blocker by itself, but I think it would be good to decide explicitly which contract we want the jwt() action to have, and then shape the implementation/docs around that.

Thanks, this makes sense to me, and I agree the main question is the contract rather than regex itself.

To make that boundary explicit, I renamed the action from jwt() to jwsCompact(). My intention here is to keep this as a lightweight structural validator for the 3-part JWS compact serialization, which also covers the common signed JWT shape, without implying broader JWT semantics.

This also leaves room to add jweCompact() later for the 5-part compact serialization if we decide to support that shape, while keeping jwt() reserved in case we ever want a broader or higher-level API in the future.

I also updated the documentation to describe the current boundaries explicitly: jwsCompact() only checks the structural format, does not decode the token, does not verify signatures, and does not validate claims. For full JWT validation and signature verification, I think dedicated libraries are the better fit, so I added a pointer to https://www.jwt.io/libraries?programming_language=javascript as well.

Given that, I think the lightweight jwsCompact() direction is a good fit for Valibot.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
library/src/actions/jwsCompact/jwsCompact.test.ts (1)

73-79: Use synthetic fixtures here instead of literal JWT samples.

Several of these fixtures are already matching secret-scanning as uncovered JWTs. Since jwsCompact() only validates structure, base64url-looking gibberish exercises the same paths without creating high-signal security noise in the test suite.

Also applies to: 117-117

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@library/src/actions/jwsCompact/jwsCompact.test.ts` around lines 73 - 79,
Replace the hard-coded JWT strings in the jwsCompact.test.ts fixtures with
synthetic, non-sensitive base64url-like strings (e.g., construct
header.payload.signature patterns programmatically) so the jwsCompact() tests
still exercise structure parsing without containing real or high-signal JWT
samples; locate the array of fixtures used by the jwsCompact tests (the entries
currently containing 'eyJ...' strings including the one ending with a trailing
dot) and substitute them with generated placeholders that mirror
"header.payload.signature" shape (keeping variations such as empty signature or
different alg fields) to preserve coverage while avoiding secret-scanning hits.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@library/src/actions/jwsCompact/jwsCompact.ts`:
- Around line 99-103: The JWS_COMPACT_REGEX used by jwsCompact (referenced via
the requirement property in the '~run' method) currently rejects compact
serializations with an empty payload; update the regex definition named
JWS_COMPACT_REGEX in library/src/regex.ts to allow an empty middle (payload)
segment (change the base64url quantifier from one-or-more to zero-or-more) so
the dataset.check in '~run' accepts empty payloads, and add a regression test
exercising jwsCompact() with a valid compact serialization whose payload encodes
to the empty string to prevent regressions; ensure the behavior in '~run' and
_addIssue remains unchanged aside from accepting the empty payload case.

In `@website/src/routes/api/`(actions)/jwsCompact/index.mdx:
- Around line 3-37: Reword the doc for the jwsCompact action to keep the public
contract purely structural: replace references to "JWT strings" and implications
of signature/semantics with language that describes only the three-part JWS
compact shape (un-padded Base64URL segments) and that an empty/absent third
segment is valid syntax; update the description, summary lines that read like
JWT semantics (mentions of "signed" or 'alg: "none"') to clarify you are not
decoding or verifying headers/ signatures and that acceptance of an empty third
segment is purely a structural allowance, not an inference about decoded header
or algorithm.

In `@website/src/routes/api/`(types)/JwsCompactAction/index.mdx:
- Around line 3-15: Update the documentation text in the JwsCompactAction MDX to
remove the broader "JWT" wording and explicitly describe structural JWS compact
validation: change lines referencing "JWT validation" and "JWT strings" to
phrases like "JWS compact validation" and "JWS compact serialized strings" so
the description matches the narrower jwsCompact contract; apply the same exact
wording changes in the JwsCompactIssue MDX so both docs consistently reference
JWS compact validation and the jwsCompact function by name.

---

Nitpick comments:
In `@library/src/actions/jwsCompact/jwsCompact.test.ts`:
- Around line 73-79: Replace the hard-coded JWT strings in the
jwsCompact.test.ts fixtures with synthetic, non-sensitive base64url-like strings
(e.g., construct header.payload.signature patterns programmatically) so the
jwsCompact() tests still exercise structure parsing without containing real or
high-signal JWT samples; locate the array of fixtures used by the jwsCompact
tests (the entries currently containing 'eyJ...' strings including the one
ending with a trailing dot) and substitute them with generated placeholders that
mirror "header.payload.signature" shape (keeping variations such as empty
signature or different alg fields) to preserve coverage while avoiding
secret-scanning hits.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d1a09372-5ad9-4a25-812f-bae4bda1b74b

📥 Commits

Reviewing files that changed from the base of the PR and between 3e0d535 and ad04c5e.

📒 Files selected for processing (15)
  • library/src/actions/index.ts
  • library/src/actions/jwsCompact/index.ts
  • library/src/actions/jwsCompact/jwsCompact.test-d.ts
  • library/src/actions/jwsCompact/jwsCompact.test.ts
  • library/src/actions/jwsCompact/jwsCompact.ts
  • library/src/regex.ts
  • packages/to-json-schema/src/converters/convertAction/convertAction.test.ts
  • packages/to-json-schema/src/converters/convertAction/convertAction.ts
  • website/src/routes/api/(actions)/jwsCompact/index.mdx
  • website/src/routes/api/(actions)/jwsCompact/properties.ts
  • website/src/routes/api/(types)/JwsCompactAction/index.mdx
  • website/src/routes/api/(types)/JwsCompactAction/properties.ts
  • website/src/routes/api/(types)/JwsCompactIssue/index.mdx
  • website/src/routes/api/(types)/JwsCompactIssue/properties.ts
  • website/src/routes/api/menu.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • library/src/regex.ts

Comment thread library/src/actions/jwsCompact/jwsCompact.ts
Comment thread website/src/routes/api/(actions)/jwsCompact/index.mdx Outdated
Comment thread website/src/routes/api/(types)/JwsCompactAction/index.mdx Outdated
@fabian-hiller
Copy link
Copy Markdown
Member

I will probably merged this now but wanted to ask if the regex can be shorten without changing it meaning or perf. Feel free to check and create a PR if so.

const JWS_COMPACT_REGEX = /^(?:[\w-]{4})*(?:[\w-]{2,4})\.(?:[\w-]{4})*(?:[\w-]{2,4})?\.(?:[\w-]{4})*(?:[\w-]{2,4})?$/u

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread library/src/actions/jwsCompact/jwsCompact.ts
@fabian-hiller fabian-hiller merged commit e7d2260 into open-circle:main Mar 17, 2026
13 of 14 checks passed
@fabian-hiller
Copy link
Copy Markdown
Member

Valibot v1.3 is available. Feel free to submit and expense of $50 for all your contributions for Valibot v1.3 via Open Collective.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants