Skip to content

add token based authantication#42

Merged
cobraprojects merged 5 commits into
mainfrom
token-based-authantication
May 14, 2026
Merged

add token based authantication#42
cobraprojects merged 5 commits into
mainfrom
token-based-authantication

Conversation

@cobraprojects
Copy link
Copy Markdown
Owner

@cobraprojects cobraprojects commented May 13, 2026

Summary by CodeRabbit

  • New Features

    • Token-based API auth added with UI pages and header links in Next/Nuxt/SvelteKit to obtain bearer tokens and fetch protected posts.
  • Refactor

    • Auth runtime and types reworked for guard-specific login/registration and a unified "authenticated user" shape.
  • Infrastructure

    • Runtime preloads discovered model modules; global registries moved to shared global scope. Seed data now uses updated password hashing.
  • Tests

    • E2E and unit tests added/expanded to validate token flows, docs, and global registry behavior.
  • Documentation

    • Token-guard and personal-access-token docs expanded with examples.
  • Chores

    • Generated auth type artifacts added to project outputs.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d909fec5-abda-4081-89e2-c804b0ba09d6

📥 Commits

Reviewing files that changed from the base of the PR and between 338d02f and a468e18.

📒 Files selected for processing (5)
  • apps/blog-next/app/api/v1/posts/route.ts
  • apps/blog-nuxt/server/api/v1/posts.get.ts
  • apps/blog-sveltekit/src/routes/api/v1/posts/+server.ts
  • packages/auth/src/runtime.ts
  • packages/auth/tests/package.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/blog-next/app/api/v1/posts/route.ts
  • apps/blog-sveltekit/src/routes/api/v1/posts/+server.ts
  • packages/auth/tests/package.test.ts

📝 Walkthrough

Walkthrough

Adds token-based API endpoints and client pages for Next/Nuxt/SvelteKit, globalizes model/morph registries on globalThis and preloads discovered model modules, refactors auth runtime and contracts for guard-scoped token/session behavior, updates CLI/type generation and adapters, extends tests and docs, and adjusts seeders/blog libs password hashing.

Changes

Combined PR checkpoint

Layer / File(s) Summary
All changes (end-to-end checkpoint)
apps/*, packages/*, tests/*, docs/*
Adds token issuance endpoints and authenticated posts endpoints; client pages/forms for token creation and post fetching; config updates to add api token guards; auth runtime and contracts refactor for token/session guard behavior; globalizes model and morph registries to globalThis and preloads discovered model modules during runtime init; CLI emits auth.d.ts type placeholder; provider/adapters updated to use AuthenticatedAuthUser; tests and docs extended; seeders and blog libs updated to hash 'secret-secret'.

Estimated code review effort:
🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs:

"I hopped through files to make tokens sing,
Forms and APIs now dance in spring,
Registries gather, preloads hum,
A rabbit cheers — new flows have come!" 🐇✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title contains a typo ('authantication' instead of 'authentication') and is vague about the scope of changes across multiple framework implementations. Correct the typo to 'add token based authentication' for clarity and professionalism.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch token-based-authantication

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

@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

🧹 Nitpick comments (4)
apps/blog-sveltekit/src/routes/api-token-posts/+page.svelte (1)

73-75: ⚡ Quick win

Consider using standard Authorization header casing.

The HTTP convention is to use Authorization with a capital 'A'. While headers are case-insensitive per the HTTP specification, using standard casing improves consistency with documentation and avoids potential issues with non-compliant middleware or proxies.

📝 Proposed fix
     const response = await fetch('/api/v1/posts', {
-      headers: token ? { authorization: `Bearer ${token}` } : undefined,
+      headers: token ? { Authorization: `Bearer ${token}` } : undefined,
     })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/blog-sveltekit/src/routes/api-token-posts/`+page.svelte around lines 73
- 75, Update the fetch call that sets the bearer token so the header uses
standard casing: change the header key from 'authorization' to 'Authorization'
in the code that constructs the request (the const response = await fetch(...)
block that conditionally sets headers using token). Ensure the conditional
remains the same (token ? { Authorization: `Bearer ${token}` } : undefined) and
that nothing else in the fetch call is altered.
apps/blog-nuxt/app/pages/api-token-posts.vue (2)

73-75: ⚡ Quick win

Consider using standard Authorization header casing.

The HTTP convention is to use Authorization with a capital 'A'. While headers are case-insensitive per the HTTP specification, using standard casing improves consistency with documentation and avoids potential issues with non-compliant middleware or proxies.

📝 Proposed fix
     const response = await fetch('/api/v1/posts', {
-      headers: token ? { authorization: `Bearer ${token}` } : undefined,
+      headers: token ? { Authorization: `Bearer ${token}` } : undefined,
     })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/blog-nuxt/app/pages/api-token-posts.vue` around lines 73 - 75, The fetch
call building the request headers uses lowercase 'authorization'; change it to
the standard 'Authorization' header name when token is present (the fetch
invocation that assigns to response and uses the token variable for Bearer auth
to '/api/v1/posts') so the headers object uses 'Authorization': `Bearer
${token}` instead of 'authorization' to ensure standard casing.

38-38: ⚡ Quick win

Use SubmitEvent for better type specificity.

Form submit handlers should use SubmitEvent instead of the generic Event type for improved type safety and clarity.

🔧 Proposed fix
-async function createToken(event: Event): Promise<void> {
+async function createToken(event: SubmitEvent): Promise<void> {
   event.preventDefault()
   if (!(event.currentTarget instanceof HTMLFormElement)) {
     return
-async function fetchPosts(event: Event): Promise<void> {
+async function fetchPosts(event: SubmitEvent): Promise<void> {
   event.preventDefault()
   if (!(event.currentTarget instanceof HTMLFormElement)) {
     return

Also applies to: 62-62

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/blog-nuxt/app/pages/api-token-posts.vue` at line 38, The submit handlers
use the generic Event type; update the function signatures to use SubmitEvent
for stronger typing — e.g., change async function createToken(event: Event):
Promise<void> to accept event: SubmitEvent, and apply the same change to the
other handler referenced (the function at the second occurrence around line 62)
so both form submit handlers use SubmitEvent for proper typings when accessing
form data and preventDefault.
apps/blog-next/app/api-token-posts/token-posts-client.tsx (1)

69-71: ⚡ Quick win

Consider using standard Authorization header casing.

The HTTP convention is to use Authorization with a capital 'A'. While headers are case-insensitive per the HTTP specification, using standard casing improves consistency with documentation and avoids potential issues with non-compliant middleware or proxies.

📝 Proposed fix
     const response = await fetch('/api/v1/posts', {
-      headers: token ? { authorization: `Bearer ${token}` } : undefined,
+      headers: token ? { Authorization: `Bearer ${token}` } : undefined,
     })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/blog-next/app/api-token-posts/token-posts-client.tsx` around lines 69 -
71, The fetch call in token-posts-client.tsx sets the header key as
"authorization" (see the headers block in the fetch call that defines response)
— change the header key to standard "Authorization" casing when token is present
(i.e., headers: token ? { Authorization: `Bearer ${token}` } : undefined) so the
Authorization header uses the conventional capital 'A' while keeping the same
token value and conditional behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/blog-next/app/api/v1/tokens/route.ts`:
- Around line 17-28: The login flow currently skips verifyPassword when
User.where('email', email) returns no user, leaking timing; fix by adding a
module-level precomputed DUMMY_PASSWORD_HASH constant (argon2/bcrypt hash of a
fixed password) and always call verifyPassword(password, actualHashOrDummy) so
that verifyPassword is invoked whether or not currentUser exists — replace the
conditional that sets passwordMatches to instead set const hashToCheck = typeof
passwordHash === 'string' ? passwordHash : DUMMY_PASSWORD_HASH and then await
verifyPassword(password, hashToCheck); keep the generic error response
unchanged.

In `@packages/core/tests/runtime.test.ts`:
- Around line 1738-1746: The test currently deletes the test-only global
__holoCoreRuntimeModelPreload only on the success path, which can leak state if
assertions fail; update the block that reads state from globalThis (the symbol
__holoCoreRuntimeModelPreload) so the expect(...) assertion is executed inside a
try block and the delete state.__holoCoreRuntimeModelPreload is moved to a
finally block to guarantee cleanup regardless of test outcome.

---

Nitpick comments:
In `@apps/blog-next/app/api-token-posts/token-posts-client.tsx`:
- Around line 69-71: The fetch call in token-posts-client.tsx sets the header
key as "authorization" (see the headers block in the fetch call that defines
response) — change the header key to standard "Authorization" casing when token
is present (i.e., headers: token ? { Authorization: `Bearer ${token}` } :
undefined) so the Authorization header uses the conventional capital 'A' while
keeping the same token value and conditional behavior.

In `@apps/blog-nuxt/app/pages/api-token-posts.vue`:
- Around line 73-75: The fetch call building the request headers uses lowercase
'authorization'; change it to the standard 'Authorization' header name when
token is present (the fetch invocation that assigns to response and uses the
token variable for Bearer auth to '/api/v1/posts') so the headers object uses
'Authorization': `Bearer ${token}` instead of 'authorization' to ensure standard
casing.
- Line 38: The submit handlers use the generic Event type; update the function
signatures to use SubmitEvent for stronger typing — e.g., change async function
createToken(event: Event): Promise<void> to accept event: SubmitEvent, and apply
the same change to the other handler referenced (the function at the second
occurrence around line 62) so both form submit handlers use SubmitEvent for
proper typings when accessing form data and preventDefault.

In `@apps/blog-sveltekit/src/routes/api-token-posts/`+page.svelte:
- Around line 73-75: Update the fetch call that sets the bearer token so the
header uses standard casing: change the header key from 'authorization' to
'Authorization' in the code that constructs the request (the const response =
await fetch(...) block that conditionally sets headers using token). Ensure the
conditional remains the same (token ? { Authorization: `Bearer ${token}` } :
undefined) and that nothing else in the fetch call is altered.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8acb7c22-8320-4c1c-bc18-cd8f20fb3296

📥 Commits

Reviewing files that changed from the base of the PR and between b653c7d and 17627e1.

📒 Files selected for processing (25)
  • apps/blog-next/app/api-token-posts/page.tsx
  • apps/blog-next/app/api-token-posts/token-posts-client.tsx
  • apps/blog-next/app/api/v1/posts/route.ts
  • apps/blog-next/app/api/v1/tokens/route.ts
  • apps/blog-next/app/layout.tsx
  • apps/blog-next/config/auth.ts
  • apps/blog-next/tests/run.mjs
  • apps/blog-nuxt/app/app.vue
  • apps/blog-nuxt/app/pages/api-token-posts.vue
  • apps/blog-nuxt/config/auth.ts
  • apps/blog-nuxt/server/api/v1/posts.get.ts
  • apps/blog-nuxt/server/api/v1/tokens.post.ts
  • apps/blog-nuxt/tests/run.mjs
  • apps/blog-sveltekit/config/auth.ts
  • apps/blog-sveltekit/src/routes/+layout.svelte
  • apps/blog-sveltekit/src/routes/api-token-posts/+page.svelte
  • apps/blog-sveltekit/src/routes/api/v1/posts/+server.ts
  • apps/blog-sveltekit/src/routes/api/v1/tokens/+server.ts
  • apps/blog-sveltekit/tests/run.mjs
  • packages/core/src/portable/holo.ts
  • packages/core/tests/runtime.test.ts
  • packages/db/src/model/ModelRegistry.ts
  • packages/db/src/model/morphRegistry.ts
  • packages/db/tests/model-core.test.ts
  • tests/example-app-token-auth-flow.mjs

Comment on lines +17 to +28
const currentUser = await User.where('email', email).first()
const passwordHash = currentUser?.get('password')
const passwordMatches = typeof passwordHash === 'string'
? await verifyPassword(password, passwordHash)
: false

if (!currentUser || !passwordMatches) {
return Response.json({
ok: false,
message: 'Invalid credentials.',
}, { status: 401 })
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

User enumeration via timing side-channel.

When the email doesn't match a user, verifyPassword is skipped and the response returns almost immediately, whereas a valid email with a wrong password incurs the full hash-verification cost (typically 100–500ms for argon2/bcrypt). Although the error message is intentionally generic, the timing delta lets an attacker enumerate valid accounts. The same pattern appears in the Nuxt and SvelteKit token endpoints.

Recommend always invoking verifyPassword against a stable dummy hash when the user is not found so the response time is independent of account existence.

🛡️ Proposed fix to equalize response timing
-  const currentUser = await User.where('email', email).first()
-  const passwordHash = currentUser?.get('password')
-  const passwordMatches = typeof passwordHash === 'string'
-    ? await verifyPassword(password, passwordHash)
-    : false
+  const currentUser = await User.where('email', email).first()
+  const passwordHash = currentUser?.get('password')
+  // Use a precomputed dummy hash so response time does not leak account existence.
+  const hashToVerify = typeof passwordHash === 'string' ? passwordHash : DUMMY_PASSWORD_HASH
+  const verified = await verifyPassword(password, hashToVerify)
+  const passwordMatches = typeof passwordHash === 'string' && verified

DUMMY_PASSWORD_HASH should be a precomputed argon2/bcrypt hash of an arbitrary string, generated once and stored as a module-level constant (not regenerated per request).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/blog-next/app/api/v1/tokens/route.ts` around lines 17 - 28, The login
flow currently skips verifyPassword when User.where('email', email) returns no
user, leaking timing; fix by adding a module-level precomputed
DUMMY_PASSWORD_HASH constant (argon2/bcrypt hash of a fixed password) and always
call verifyPassword(password, actualHashOrDummy) so that verifyPassword is
invoked whether or not currentUser exists — replace the conditional that sets
passwordMatches to instead set const hashToCheck = typeof passwordHash ===
'string' ? passwordHash : DUMMY_PASSWORD_HASH and then await
verifyPassword(password, hashToCheck); keep the generic error response
unchanged.

Comment thread packages/core/tests/runtime.test.ts
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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/blog-sveltekit/src/routes/api/v1/tokens/`+server.ts:
- Around line 34-39: The JSON response that returns the plaintext bearer token
(token.plainTextToken) and tokenId is currently cacheable; update the handler
that returns json({...}) in +server.ts so the response includes a Cache-Control:
no-store header to prevent browser/service-worker/intermediary caching. Modify
the return json(...) call to pass response headers (e.g., json(payload, {
headers: { 'Cache-Control': 'no-store' } })) ensuring the token.plainTextToken
and token.id are still returned but never stored by caches.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 996e7426-b416-4461-a87e-605846efcb36

📥 Commits

Reviewing files that changed from the base of the PR and between 17627e1 and 8e7c350.

📒 Files selected for processing (11)
  • apps/blog-next/app/api-token-posts/token-posts-client.tsx
  • apps/blog-next/app/api/v1/tokens/route.ts
  • apps/blog-nuxt/app/pages/api-token-posts.vue
  • apps/blog-nuxt/server/api/v1/tokens.post.ts
  • apps/blog-sveltekit/src/routes/api-token-posts/+page.svelte
  • apps/blog-sveltekit/src/routes/api/v1/tokens/+server.ts
  • packages/auth/src/runtime.ts
  • packages/auth/tests/package.test.ts
  • packages/core/tests/runtime.test.ts
  • packages/db/tests/type-system.test.ts
  • tests/example-app-token-auth-flow.mjs
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/blog-nuxt/app/pages/api-token-posts.vue
  • tests/example-app-token-auth-flow.mjs
  • apps/blog-sveltekit/src/routes/api-token-posts/+page.svelte
  • apps/blog-next/app/api-token-posts/token-posts-client.tsx
  • apps/blog-nuxt/server/api/v1/tokens.post.ts
  • apps/blog-next/app/api/v1/tokens/route.ts
  • packages/core/tests/runtime.test.ts

Comment thread apps/blog-sveltekit/src/routes/api/v1/tokens/+server.ts
@cobraprojects
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 7

Caution

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

⚠️ Outside diff range comments (1)
packages/auth/src/contracts.ts (1)

244-282: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Token guards still expose session-only APIs.

AuthTokenGuardFacade inherits loginUsing*, impersonate*, impersonation(), and stopImpersonating() from AuthGuardFacade. Those calls still route through session-only runtime paths, so auth.guard('api') type-checks methods that can only fail at runtime. Split the shared guard surface from the session-only surface before specializing the token facade.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/auth/src/contracts.ts` around lines 244 - 282, The token guard
exposes session-only APIs because AuthTokenGuardFacade inherits session methods
from AuthGuardFacade; extract a shared base interface (e.g. AuthBaseGuardFacade)
containing purely token-agnostic methods (check, user, provider, id,
currentAccessToken, login, register, refreshUser?, logout, etc.), move the
session-only APIs (loginUsing, loginUsingId, impersonate, impersonateById,
impersonation, stopImpersonating) into a new AuthSessionOnlyFacade, then have
AuthSessionGuardFacade extend AuthBaseGuardFacade + AuthSessionOnlyFacade while
changing AuthTokenGuardFacade to extend only AuthBaseGuardFacade; update
references to AuthGuardFacade accordingly (rename or replace) so token guards no
longer inherit the session methods.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/blog-next/app/api/v1/tokens/route.ts`:
- Around line 27-32: The response that returns the plaintext token (the
Response.json call in apps/blog-next/app/api/v1/tokens/route.ts which returns
token.plainTextToken, token.id, and token.abilities) must include a
Cache-Control: no-store header to prevent browsers or intermediaries from
caching the sensitive token; update the Response.json invocation to include a
headers object with "Cache-Control": "no-store" so the plaintext token is never
cached.

In `@apps/blog-next/config/auth.ts`:
- Around line 45-46: Change the personalAccessTokens.defaultAbilities to an
empty array and update token issuance code to pass explicit abilities when
creating tokens: set personalAccessTokens.defaultAbilities = [] in the auth
config and modify calls like auth.guard('api').login(submission.data) to include
an explicit abilities array (e.g., auth.guard('api').login({...submission.data,
abilities: [...]}) or supply abilities as the appropriate parameter), and
propagate the same config change and explicit-abilities update across the other
token routes/examples that issue personal access tokens so no token inherits the
old posts.read default.

In `@apps/blog-nuxt/server/api/v1/tokens.post.ts`:
- Around line 28-34: The response that returns token.plainTextToken must include
a Cache-Control: no-store header to prevent caching; before returning the object
in the POST handler (the block that returns ok:true, token:
token.plainTextToken, tokenId: token.id, abilities: token.abilities) set the
response header—e.g. call event.res.setHeader('Cache-Control','no-store') (or
the framework-specific response.setHeader) immediately prior to the return so
the plaintext bearer token is never cached by browsers or intermediaries.

In `@apps/blog-sveltekit/src/lib/server/blog.ts`:
- Around line 18-22: The code currently bootstraps a repo-known password by
calling hashPassword('secret-secret') and creating the account via
User.unguarded(() => User.create(...)) for editor@example.com; change this to
read the bootstrap password from configuration/environment (e.g.,
process.env.BOOTSTRAP_EDITOR_PASSWORD) and only create the user with that hashed
value if the env var is present, otherwise create the account with an
unusable/locked password (or a random hash and a flag requiring reset) so no
repo-known credential exists; update the same pattern in any companion blog
helpers and seeders that call hashPassword or create the editor user to use the
env-configured password or an unusable password path instead.

In `@packages/auth/src/contracts.ts`:
- Around line 176-178: The exported conditional type AuthGuardFacadeFor<TName
extends string> currently hard-codes the 'api' case to AuthTokenGuardFacade;
remove that special-case so the mapping is driven solely by the registry types
(i.e., rely on AuthGuardFacadeForDriver<RegisteredAuthGuardDriver<TName>> and
RegisteredAuthGuardDriver) so consumers who register 'api' as a different guard
get correct types; update the exported type definition for AuthGuardFacadeFor to
drop the 'api' branch and ensure examples/docs show augmenting
HoloAuth.TypeRegistry instead of relying on the built-in override.

In `@packages/auth/src/runtime.ts`:
- Around line 1600-1609: registerForGuard currently persists the user for token
guards via registerUserForGuard(guardName, input) then calls
createLoginTokenForGuard; if token creation fails the new account is left
behind. Modify registerForGuard so that when guard.driver === 'token' you
ensureTokenStore(), call registerUserForGuard, then attempt
createLoginTokenForGuard inside a try/catch; on any error delete/rollback the
just-created user using the same cleanup used by the email-verification path
(e.g., the existing unregister/delete user helper used elsewhere), rethrow the
error, and keep successful token return unchanged. Ensure you reference
registerForGuard, registerUserForGuard, createLoginTokenForGuard and
ensureTokenStore when making the change.
- Around line 1596-1598: The top-level facade currently assumes a session-backed
default but registerDefaultUser (and login) use
getRuntimeBindings().config.defaults.guard directly; add a runtime validation
that config.defaults.guard is session-backed and refuse/throw during startup
configuration if it points to a token-based driver, or alternatively make the
top-level AuthFacade proxy guard-aware and route register/login to the specific
guard driver. Concretely: in the runtime initialization (where
getRuntimeBindings or config is validated) check config.defaults.guard against
the guards map (e.g., config.guards[<name>].driver) and either throw a clear
error preventing token-based defaults or change
AuthFacade/registerDefaultUser/login to delegate to
auth.guard(default).register()/login() so behavior matches
auth.guard(default).register(); apply the same validation/delegation logic to
the other call sites mentioned (the login path and the other register wrappers).

---

Outside diff comments:
In `@packages/auth/src/contracts.ts`:
- Around line 244-282: The token guard exposes session-only APIs because
AuthTokenGuardFacade inherits session methods from AuthGuardFacade; extract a
shared base interface (e.g. AuthBaseGuardFacade) containing purely
token-agnostic methods (check, user, provider, id, currentAccessToken, login,
register, refreshUser?, logout, etc.), move the session-only APIs (loginUsing,
loginUsingId, impersonate, impersonateById, impersonation, stopImpersonating)
into a new AuthSessionOnlyFacade, then have AuthSessionGuardFacade extend
AuthBaseGuardFacade + AuthSessionOnlyFacade while changing AuthTokenGuardFacade
to extend only AuthBaseGuardFacade; update references to AuthGuardFacade
accordingly (rename or replace) so token guards no longer inherit the session
methods.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5b736c43-a60c-40bd-ac1c-e485feaaf98b

📥 Commits

Reviewing files that changed from the base of the PR and between 8e7c350 and ad08425.

📒 Files selected for processing (24)
  • apps/blog-next/app/api-token-posts/token-posts-client.tsx
  • apps/blog-next/app/api/v1/tokens/route.ts
  • apps/blog-next/config/auth.ts
  • apps/blog-next/server/db/seeders/BlogSeeder.ts
  • apps/blog-next/server/lib/blog.ts
  • apps/blog-nuxt/app/pages/api-token-posts.vue
  • apps/blog-nuxt/config/auth.ts
  • apps/blog-nuxt/server/api/v1/tokens.post.ts
  • apps/blog-nuxt/server/db/seeders/BlogSeeder.ts
  • apps/blog-nuxt/server/lib/blog.ts
  • apps/blog-sveltekit/config/auth.ts
  • apps/blog-sveltekit/server/db/seeders/BlogSeeder.ts
  • apps/blog-sveltekit/src/lib/server/blog.ts
  • apps/blog-sveltekit/src/routes/api-token-posts/+page.svelte
  • apps/blog-sveltekit/src/routes/api/v1/tokens/+server.ts
  • apps/docs/docs/auth/guards-and-providers.md
  • apps/docs/docs/auth/personal-access-tokens.md
  • packages/auth/src/contracts.ts
  • packages/auth/src/index.ts
  • packages/auth/src/runtime.ts
  • packages/auth/tests/contracts.type.test.ts
  • packages/auth/tests/docs-smoke.test.ts
  • packages/auth/tests/package.test.ts
  • tests/example-app-token-auth-flow.mjs
✅ Files skipped from review due to trivial changes (1)
  • apps/docs/docs/auth/guards-and-providers.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/blog-sveltekit/src/routes/api-token-posts/+page.svelte
  • apps/blog-next/app/api-token-posts/token-posts-client.tsx
  • tests/example-app-token-auth-flow.mjs
  • apps/blog-nuxt/app/pages/api-token-posts.vue

Comment thread apps/blog-next/app/api/v1/tokens/route.ts
Comment thread apps/blog-next/config/auth.ts Outdated
Comment thread apps/blog-nuxt/server/api/v1/tokens.post.ts
Comment on lines +18 to +22
const password = await hashPassword('secret-secret')
const user = await User.unguarded(() => User.create({
name: 'Holo Editor',
email: 'editor@example.com',
password: 'secret',
password,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid bootstrapping a repo-known password at runtime.

On an empty deployment this helper creates editor@example.com with a password published in the repo. With the new token auth flow, that becomes a reusable way to mint API tokens. Please source the bootstrap password from environment/config, or create the account with an unusable password until an operator sets it. The same fix should be applied to the companion blog helpers and seeders as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/blog-sveltekit/src/lib/server/blog.ts` around lines 18 - 22, The code
currently bootstraps a repo-known password by calling
hashPassword('secret-secret') and creating the account via User.unguarded(() =>
User.create(...)) for editor@example.com; change this to read the bootstrap
password from configuration/environment (e.g.,
process.env.BOOTSTRAP_EDITOR_PASSWORD) and only create the user with that hashed
value if the env var is present, otherwise create the account with an
unusable/locked password (or a random hash and a flag requiring reset) so no
repo-known credential exists; update the same pattern in any companion blog
helpers and seeders that call hashPassword or create the editor user to use the
env-configured password or an unusable password path instead.

Comment thread packages/auth/src/contracts.ts Outdated
Comment thread packages/auth/src/runtime.ts Outdated
Comment thread packages/auth/src/runtime.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

♻️ Duplicate comments (1)
packages/auth/src/runtime.ts (1)

1621-1628: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Token-guard registration rollback misses the model-instance delete fallback.

On token registration failure, registerForGuard() calls rollbackSerializedUserForGuard(), but that helper only tries adapter.delete. This misses the createdUser.delete() fallback already handled in rollbackRegisteredUser(), so failed token issuance can still leave a persisted user for adapters without delete.

💡 Suggested fix
 async function rollbackSerializedUserForGuard(guardName: string, user: SerializedAuthUser): Promise<void> {
   const guard = getGuardConfig(guardName)
   const { adapter } = getProviderAdapter(guard.provider)
+  const created = await adapter.findById(user.id).catch(() => null)
+  if (created) {
+    await rollbackRegisteredUser(adapter, created, user)
+    return
+  }
   if (adapter.delete) {
     await adapter.delete(user.id)
   }
 }

Also applies to: 1665-1670

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/auth/src/runtime.ts` around lines 1621 - 1628, The catch block after
createLoginTokenForGuard(guardName, user, input) only calls
rollbackSerializedUserForGuard(guardName, user) which relies solely on
adapter.delete; update the rollback to also attempt the model-instance delete
fallback used by rollbackRegisteredUser (or directly call
rollbackRegisteredUser(guardName, user)) so adapters without delete won't leave
a persisted user; apply the same change for the similar block around lines
1665-1670 to ensure both token-guard paths perform the adapter.delete then
createdUser.delete fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/blog-nuxt/server/api/v1/posts.get.ts`:
- Line 30: The ownership filter currently coerces userId to a number with
Number(userId) in the .where('user_id', ...) call which can produce NaN for
non-numeric IDs; update the .where call to use the native id value (e.g.,
currentUser.id or userId as-is) instead of Number(userId) so the query compares
the actual identifier type returned by your auth layer; locate the .where
invocation in posts.get.ts (the .where('user_id', Number(userId)) call) and
replace the numeric coercion with the original id value.

In `@apps/blog-sveltekit/src/routes/api/v1/posts/`+server.ts:
- Line 27: The ownership filter currently coerces userId with Number(userId) in
the query builder (the .where('user_id', Number(userId)) call), which can
produce NaN for non-numeric IDs; change the .where call to use the native id
value instead (e.g., pass userId or currentUser.id directly) so the query uses
the actual identifier type and avoid numeric coercion that can break filtering.

---

Duplicate comments:
In `@packages/auth/src/runtime.ts`:
- Around line 1621-1628: The catch block after
createLoginTokenForGuard(guardName, user, input) only calls
rollbackSerializedUserForGuard(guardName, user) which relies solely on
adapter.delete; update the rollback to also attempt the model-instance delete
fallback used by rollbackRegisteredUser (or directly call
rollbackRegisteredUser(guardName, user)) so adapters without delete won't leave
a persisted user; apply the same change for the similar block around lines
1665-1670 to ensure both token-guard paths perform the adapter.delete then
createdUser.delete fallback.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 428dc1fd-0112-4cff-8ce4-c094466f5260

📥 Commits

Reviewing files that changed from the base of the PR and between ad08425 and 338d02f.

📒 Files selected for processing (28)
  • apps/blog-next/app/api/v1/posts/route.ts
  • apps/blog-next/app/api/v1/tokens/route.ts
  • apps/blog-next/config/auth.ts
  • apps/blog-nuxt/config/auth.ts
  • apps/blog-nuxt/server/api/v1/posts.get.ts
  • apps/blog-nuxt/server/api/v1/tokens.post.ts
  • apps/blog-sveltekit/config/auth.ts
  • apps/blog-sveltekit/src/routes/api/v1/posts/+server.ts
  • apps/blog-sveltekit/src/routes/api/v1/tokens/+server.ts
  • apps/docs/docs/auth/guards-and-providers.md
  • apps/docs/docs/auth/personal-access-tokens.md
  • packages/adapter-nuxt/src/module.ts
  • packages/adapter-nuxt/tests/module.test.ts
  • packages/adapter-nuxt/tests/setup.test.ts
  • packages/auth-clerk/src/contracts.ts
  • packages/auth-clerk/src/index.ts
  • packages/auth-workos/src/contracts.ts
  • packages/auth-workos/src/index.ts
  • packages/auth/src/contracts.ts
  • packages/auth/src/index.ts
  • packages/auth/src/runtime.ts
  • packages/auth/src/runtime/context.ts
  • packages/auth/tests/contracts.type.test.ts
  • packages/auth/tests/docs-smoke.test.ts
  • packages/auth/tests/package.test.ts
  • packages/cli/src/project/registry.ts
  • packages/cli/src/project/shared.ts
  • packages/cli/tests/authorization-registry.test.ts
✅ Files skipped from review due to trivial changes (2)
  • apps/docs/docs/auth/guards-and-providers.md
  • packages/cli/src/project/shared.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/auth/tests/docs-smoke.test.ts
  • apps/blog-next/app/api/v1/posts/route.ts
  • packages/auth/src/index.ts
  • apps/blog-nuxt/server/api/v1/tokens.post.ts
  • packages/auth/tests/contracts.type.test.ts
  • packages/auth/tests/package.test.ts
  • apps/blog-next/app/api/v1/tokens/route.ts

Comment thread apps/blog-nuxt/server/api/v1/posts.get.ts Outdated
Comment thread apps/blog-sveltekit/src/routes/api/v1/posts/+server.ts Outdated
@cobraprojects
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cobraprojects cobraprojects merged commit 0213a47 into main May 14, 2026
1 check passed
@cobraprojects cobraprojects deleted the token-based-authantication branch May 14, 2026 01:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant