Skip to content

feat(core): add experimental Valibot schema validation#160

Merged
halvaradop merged 3 commits intomasterfrom
feat/add-experimental-valibot
May 2, 2026
Merged

feat(core): add experimental Valibot schema validation#160
halvaradop merged 3 commits intomasterfrom
feat/add-experimental-valibot

Conversation

@halvaradop
Copy link
Copy Markdown
Member

@halvaradop halvaradop commented May 1, 2026

Description

This pull request introduces experimental support for Valibot schemas alongside Zod for defining and validating user identities in Aura Auth.

Valibot schemas can now be used through the identity.schema configuration to validate identity data across internal authentication flows, including default fields such as:

  • sub
  • name
  • email
  • image

as well as any additional custom fields defined by the consumer.

Aura Auth internally validates and verifies identity payloads during authentication and session flows.

With this update, the identity.schema option now supports:

  • Zod schemas
  • Valibot schemas

This validation layer is used to ensure the integrity and consistency of user identity data throughout the authentication lifecycle.


Experimental Status

Warning

Valibot support is currently experimental and may introduce unexpected behavior or incomplete type inference in some flows.

At the moment, the internal router used by Aura Auth does not fully support Valibot schema inference across all endpoints.

Known limitation:

  • POST /auth/session may not infer the complete schema correctly when using Valibot

Other authentication endpoints are fully supported.

This limitation will be addressed as router-level Valibot support continues to evolve.


Changes

  • Add experimental Valibot support for identity schemas
  • Extend createAuth to accept Valibot-based identity definitions
  • Enable identity validation using either Zod or Valibot schemas

usage

import * as valibot from "valibot"
import { createAuth } from "@aura-stack/auth"

const auth = createAuth({
  oauth: [],
  identity: {
    schema: valibot.object({
      // ...
    }),
  },
})

@halvaradop halvaradop added feature New functionality experimental labels May 1, 2026
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
auth Skipped Skipped May 2, 2026 11:36pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Warning

Rate limit exceeded

@halvaradop has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes and 21 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8f50206a-bf57-4ddd-97b4-5a9a180e2ee9

📥 Commits

Reviewing files that changed from the base of the PR and between a9cb3cc and 054ffcd.

📒 Files selected for processing (6)
  • packages/core/src/actions/signIn/authorization.ts
  • packages/core/src/actions/updateSession/updateSession.ts
  • packages/core/src/session/stateless.ts
  • packages/core/src/shared/assert.ts
  • packages/core/src/shared/identity.ts
  • packages/core/test/types.test-d.ts
📝 Walkthrough

Walkthrough

Adds experimental Valibot schema support and generalizes identity/type utilities from Zod-only to a unified Identities/FromShapeToObject model; updates runtime schema handling, type declarations, tests, and adds valibot as a dependency.

Changes

Valibot + Identity Generalization

Layer / File(s) Summary
Type primitives / shapes
packages/core/src/@types/utility.ts
Introduce Valibot-aware shape/type aliases: EditableShapeValibot, EditableShapeZod, ConfigSchema, ValibotShapeToObject, and FromShapeToObject.
Identity definitions
packages/core/src/shared/identity.ts
Add UserIdentityValibot, UserShapeValibot, Identities union; make createIdentity conditional (Zod vs Valibot) with ReturnShapeType<S>.
Schema assertions
packages/core/src/shared/assert.ts
Add runtime type guards: isValibotSchema, isValibotEntries, isZodSchema, isZodEntries.
Schema handling / validation
packages/core/src/schema-registry.ts
Generalize stripUnknownKeys and createSchemaRegistry to accept/parse both Zod and Valibot schemas; branch parse/result handling and error details per schema kind.
Core public types
packages/core/src/@types/config.ts, packages/core/src/@types/session.ts
Replace EditableShape<UserShape> constraints with Identities; switch mappings from ZodShapeToObject<...> to FromShapeToObject<...>; update ConfigSchema/IdentityConfig to accept Valibot schemas.
API / wiring
packages/core/src/createAuth.ts, packages/core/src/router/context.ts, packages/core/src/session/strategy.ts, packages/core/src/shared/logger.ts
Update generics and casting/site typings to use Identities / FromShapeToObject; adjust imports to new utility types.
Runtime tweak (defensive)
packages/core/src/actions/updateSession/updateSession.ts
Request body schema uses UserIdentity.partial().optional() and updateSessionAction accesses ctx.body defensively (ctx.body?.user, ctx.body?.expires?.toISOString()).
Tests / types
packages/core/test/identity.test.ts, packages/core/test/types.test-d.ts
Add Vitest suite and expand type tests to cover Valibot and Zod cases; update type assertion expectations to use Identities and FromShapeToObject (introduce Shapes union).
Dependency & changelog
packages/core/package.json, packages/core/CHANGELOG.md
Add valibot runtime dependency and note experimental Valibot support in changelog.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇
I hopped through types both new and old,
Zod and Valibot in my hold,
Generics stitched where once was one,
Now two schemas dance beneath the sun,
A rabbit's patch: typings freshly rolled!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding experimental Valibot schema validation to the core package.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-experimental-valibot

Tip

💬 Introducing Slack Agent: Turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value).


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
Review rate limit: 0/1 reviews remaining, refill in 43 minutes and 21 seconds.

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

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 (2)
packages/core/test/types.test-d.ts (1)

19-37: ⚡ Quick win

Add a concrete Valibot case instead of only asserting through Shapes.

These assertions can still pass through the default Zod path, so they don't actually prove that createAuth({ identity: { schema: ... } }) resolves the Valibot branch correctly. A dedicated UserIdentityValibot instantiation here would give the experimental feature real type coverage instead of relying on a union alias.

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

In `@packages/core/test/types.test-d.ts` around lines 19 - 37, Add a dedicated
Valibot test case instead of only using the union alias Shapes: create a
concrete Valibot identity (e.g., a UserIdentityValibot type or inline identity
object with a Valibot schema) and call createAuth({ identity: { schema: /*
valibot schema */ } }) (or createAuth({ oauth: [], identity: ... })) then add
expectTypeOf assertions for the resulting instance methods (api.getSession,
api.updateSession, jose.signJWS, jose.verifyJWS) to ensure they resolve to the
Valibot-mapped types (reference ValibotShapeToObject, UserShapeValibot,
AuthConfig, and FromShapeToObject/Identities) so the Valibot branch is covered
explicitly.
packages/core/src/@types/utility.ts (1)

5-5: ⚡ Quick win

Use type-only import for UserShapeValibot.

UserShapeValibot is only used as a type constraint in line 33. In a types-only file, prefer import type for consistency with line 4 and to avoid pulling in runtime code unnecessarily.

♻️ Proposed fix
-import { UserShapeValibot } from "@/shared/identity.ts"
+import type { UserShapeValibot } from "@/shared/identity.ts"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/`@types/utility.ts at line 5, The import for
UserShapeValibot is only used as a type, so change the runtime import to a
type-only import: replace the current import of UserShapeValibot with an "import
type" declaration to match the existing type-only imports (see the import at top
of the file) and satisfy the type constraint usage of UserShapeValibot in the
utility type around the code that references it (the type usage on/near line
33).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/actions/updateSession/updateSession.ts`:
- Around line 27-28: In updateSession (the code building the session payload
where you already changed ctx.body?.user), also null-guard the expires access:
replace ctx.body.expires with an optional-chained access and protect the
toISOString call (e.g., use ctx.body?.expires?.toISOString()) so that when
ctx.body is undefined you won't attempt to read expires or call toISOString;
update the session construction that references ctx.body.expires accordingly.

In `@packages/core/src/createAuth.ts`:
- Around line 79-80: The createAuth generic currently lacks a default which
widens calls like createAuth({ oauth: [] }) to the whole Identities union;
change the signature of createAuth<Identity extends Identities> to provide the
built-in Zod identity as the default (e.g. createAuth<Identity extends
Identities = BuiltInZodIdentity>) so that when callers omit the generic they get
the Zod-based identity shape; keep the rest of the implementation (creating
authInstance via createAuthInstance<Identity>(config) and the
AuthInstance<FromShapeToObject<Identity>> cast) the same, and apply the same
default pattern wherever createAuth is meant to be directly callable with the
built-in identity.

In `@packages/core/src/shared/identity.ts`:
- Around line 34-39: The createIdentity function's Valibot branch never matches
because raw Valibot entries don't carry Symbol.for("valibot"), so the code
always falls through to return z.object(shape); fix by detecting Valibot entries
and returning a Valibot schema: inside createIdentity (and when handling
UserShapeValibot) check for the Valibot structure (e.g., shape is an object of
entries or lacks the valibot symbol) and wrap those raw entries with
valibot.object(shape) before returning; keep the existing Symbol.for("valibot")
path for already-constructed Valibot schemas and retain z.object(shape) only for
Zod shapes.

---

Nitpick comments:
In `@packages/core/src/`@types/utility.ts:
- Line 5: The import for UserShapeValibot is only used as a type, so change the
runtime import to a type-only import: replace the current import of
UserShapeValibot with an "import type" declaration to match the existing
type-only imports (see the import at top of the file) and satisfy the type
constraint usage of UserShapeValibot in the utility type around the code that
references it (the type usage on/near line 33).

In `@packages/core/test/types.test-d.ts`:
- Around line 19-37: Add a dedicated Valibot test case instead of only using the
union alias Shapes: create a concrete Valibot identity (e.g., a
UserIdentityValibot type or inline identity object with a Valibot schema) and
call createAuth({ identity: { schema: /* valibot schema */ } }) (or createAuth({
oauth: [], identity: ... })) then add expectTypeOf assertions for the resulting
instance methods (api.getSession, api.updateSession, jose.signJWS,
jose.verifyJWS) to ensure they resolve to the Valibot-mapped types (reference
ValibotShapeToObject, UserShapeValibot, AuthConfig, and
FromShapeToObject/Identities) so the Valibot branch is covered explicitly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e3583353-7e79-486a-8dd1-5bd97041d499

📥 Commits

Reviewing files that changed from the base of the PR and between 66e51a6 and cda0523.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • packages/core/package.json
  • packages/core/src/@types/config.ts
  • packages/core/src/@types/session.ts
  • packages/core/src/@types/utility.ts
  • packages/core/src/actions/updateSession/updateSession.ts
  • packages/core/src/createAuth.ts
  • packages/core/src/router/context.ts
  • packages/core/src/session/strategy.ts
  • packages/core/src/shared/identity.ts
  • packages/core/src/shared/logger.ts
  • packages/core/test/types.test-d.ts

Comment thread packages/core/src/actions/updateSession/updateSession.ts Outdated
Comment thread packages/core/src/createAuth.ts Outdated
Comment thread packages/core/src/shared/identity.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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/@types/config.ts (1)

349-353: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

RouterGlobalContext.identity.schema is still Zod-only.

The IdentityConfig type (line 287) was updated to accept ZodObject<any> | ObjectSchema<any, undefined>, but RouterGlobalContext.identity.schema on line 351 remains hardcoded to ZodObject<any>. This creates a type inconsistency when Valibot schemas are used at runtime.

🐛 Proposed fix
     identity: {
         unknownKeys: "passthrough" | "strict" | "strip"
-        schema: ZodObject<any>
+        schema: ZodObject<any> | ObjectSchema<any, undefined>
         skipValidation?: boolean
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/`@types/config.ts around lines 349 - 353, Update the
RouterGlobalContext.identity.schema type to match IdentityConfig by allowing
both Zod and Valibot schemas: change the hardcoded ZodObject<any> to the union
type used in IdentityConfig (e.g., ZodObject<any> | ObjectSchema<any,
undefined>) so RouterGlobalContext.identity.schema and IdentityConfig share the
same schema type; ensure you reference the same union type wherever
RouterGlobalContext.identity.schema is declared to eliminate the type mismatch.
🧹 Nitpick comments (2)
packages/core/src/schema-registry.ts (1)

50-56: ⚡ Quick win

Valibot validation errors lose detail information.

When Valibot validation fails, details is an empty object {} (line 51), and cause is undefined (line 53). This discards useful error information that could help developers debug validation failures. Valibot provides error formatting utilities like flatten that could be used similarly to formatZodError.

♻️ Proposed fix to include Valibot error details
+import { flatten } from "valibot"
+
+const formatValibotError = (error: any) => {
+    return flatten(error.issues)
+}
+
 const parse = async (data: unknown = {}) => {
     const isZod = isZodSchema(schema)
     const parsed: any = isZod ? await schema.safeParseAsync(data) : await safeParseAsync(schema as any, data)
     if (!parsed.success) {
-        const details = JSON.stringify(isZod ? formatZodError(parsed.error) : {}, null, 2)
+        const details = JSON.stringify(isZod ? formatZodError(parsed.error) : formatValibotError(parsed), null, 2)
         throw new AuthValidationError("INVALID_IDENTITY_VALIDATION_FAILED", details, {
-            cause: isZod ? parsed.error : undefined,
+            cause: isZod ? parsed.error : parsed.issues,
         })
     }
     return isZod ? parsed.data : parsed.output
 }

Apply the same pattern to parseAsPartial (lines 59-69).

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

In `@packages/core/src/schema-registry.ts` around lines 50 - 56, The Valibot
branch currently discards useful error info: when parsed.success is false and
isZod is false the code builds details as an empty object and sets cause
undefined; update the error handling in the parsed failure branch (and mirror
the same change in parseAsPartial) to include Valibot's formatted errors (e.g.,
use valibot.flatten or equivalent flattening/formatting util) so details
contains the readable error structure and pass the original parsed.error as the
cause to AuthValidationError; keep the existing Zod handling (formatZodError and
cause) for isZod true and apply the Valibot formatting when isZod is false so
both details and cause carry useful info.
packages/core/test/identity.test.ts (1)

128-138: 💤 Low value

Misleading test data comment in passthrough tests.

The extraKey value is "should be stripped" (line 113), but in passthrough mode tests (lines 128-138, 160-170), this key is intentionally preserved. The comment is misleading given the test context.

✏️ Suggested improvement

Consider updating the test payload or using a more neutral value:

 const payload = {
     sub: "user123",
     name: "John Doe",
     role: "admin",
-    extraKey: "should be stripped",
+    extraKey: "extra value",
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/test/identity.test.ts` around lines 128 - 138, The test data
uses payload.extraKey = "should be stripped" which is misleading for the
passthrough-mode tests; update the test payload or its comment so the value
reflects that passthrough preserves unknown keys (e.g., change the string to
"preserved in passthrough" or a neutral value) and ensure assertions in the "zod
schema with 'passthrough' unknownKeys" test that calls
stripUnknownKeys(zodSchema, "passthrough") remain accurate; locate the payload
variable and the test by the identifiers payload, zodSchema, and the test name
to make the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/shared/assert.ts`:
- Around line 193-195: The runtime check in isZodSchema currently only tests for
"_def" which matches any Zod type, but the function claims to return "value is
ZodObject"; either make the runtime check actually detect an object schema
(e.g., check typeof value === "object" && value !== null && "_def" in value &&
(value as any)._def?.typeName === "ZodObject") and keep the return type
ZodObject, or change the return type to ZodTypeAny to reflect the existing broad
"_def" check; update the function signature and the runtime guard accordingly so
the type predicate and runtime behavior align (references: isZodSchema,
ZodObject, ZodTypeAny, "_def", _def.typeName).

In `@packages/core/src/shared/identity.ts`:
- Line 1: Replace the incorrect Zod destructured imports with the canonical z
import and update schema usages: remove imports of email, string, and ZodObject
and import only z (import { z } from "zod"), then update the UserIdentity schema
to call z.string() and z.email() instead of string() and email(), and update any
type references that expected ZodObject to use the z.ZodObject type or infer
with z.infer as appropriate.

---

Outside diff comments:
In `@packages/core/src/`@types/config.ts:
- Around line 349-353: Update the RouterGlobalContext.identity.schema type to
match IdentityConfig by allowing both Zod and Valibot schemas: change the
hardcoded ZodObject<any> to the union type used in IdentityConfig (e.g.,
ZodObject<any> | ObjectSchema<any, undefined>) so
RouterGlobalContext.identity.schema and IdentityConfig share the same schema
type; ensure you reference the same union type wherever
RouterGlobalContext.identity.schema is declared to eliminate the type mismatch.

---

Nitpick comments:
In `@packages/core/src/schema-registry.ts`:
- Around line 50-56: The Valibot branch currently discards useful error info:
when parsed.success is false and isZod is false the code builds details as an
empty object and sets cause undefined; update the error handling in the parsed
failure branch (and mirror the same change in parseAsPartial) to include
Valibot's formatted errors (e.g., use valibot.flatten or equivalent
flattening/formatting util) so details contains the readable error structure and
pass the original parsed.error as the cause to AuthValidationError; keep the
existing Zod handling (formatZodError and cause) for isZod true and apply the
Valibot formatting when isZod is false so both details and cause carry useful
info.

In `@packages/core/test/identity.test.ts`:
- Around line 128-138: The test data uses payload.extraKey = "should be
stripped" which is misleading for the passthrough-mode tests; update the test
payload or its comment so the value reflects that passthrough preserves unknown
keys (e.g., change the string to "preserved in passthrough" or a neutral value)
and ensure assertions in the "zod schema with 'passthrough' unknownKeys" test
that calls stripUnknownKeys(zodSchema, "passthrough") remain accurate; locate
the payload variable and the test by the identifiers payload, zodSchema, and the
test name to make the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 015287c0-d4ff-4671-9b53-b5f26d6dd313

📥 Commits

Reviewing files that changed from the base of the PR and between cda0523 and a9cb3cc.

📒 Files selected for processing (9)
  • packages/core/CHANGELOG.md
  • packages/core/src/@types/config.ts
  • packages/core/src/actions/updateSession/updateSession.ts
  • packages/core/src/createAuth.ts
  • packages/core/src/schema-registry.ts
  • packages/core/src/shared/assert.ts
  • packages/core/src/shared/identity.ts
  • packages/core/test/identity.test.ts
  • packages/core/test/types.test-d.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/core/CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/createAuth.ts

Comment thread packages/core/src/shared/assert.ts Outdated
Comment thread packages/core/src/shared/identity.ts Outdated
@halvaradop halvaradop merged commit 40e0c83 into master May 2, 2026
7 checks passed
@halvaradop halvaradop deleted the feat/add-experimental-valibot branch May 2, 2026 23:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

experimental feature New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant