Skip to content

add workos logic#35

Merged
cobraprojects merged 3 commits into
mainfrom
fix-model-naming
May 11, 2026
Merged

add workos logic#35
cobraprojects merged 3 commits into
mainfrom
fix-model-naming

Conversation

@cobraprojects
Copy link
Copy Markdown
Owner

@cobraprojects cobraprojects commented May 11, 2026

Summary by CodeRabbit

  • New Features

    • WorkOS authentication added: sign-in, register, callback, and hosted logout flows across Next.js, Nuxt, and SvelteKit apps.
  • Updates

    • Login/register UIs now include WorkOS options; header/nav include WorkOS logout forms.
    • Auth config simplified: removed socialEncryptionKey, added provider selector, streamlined WorkOS settings.
  • Documentation

    • WorkOS integration docs rewritten with new examples and mapping guidance.
  • Tests

    • Test suites and fixtures updated to cover WorkOS flows and config changes.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Warning

Rate limit exceeded

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

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1b62e1b0-a339-45f8-81bb-eba159b6fb88

📥 Commits

Reviewing files that changed from the base of the PR and between aa42580 and b88ff0a.

📒 Files selected for processing (2)
  • packages/auth-workos/src/contracts.ts
  • packages/auth-workos/tests/package.test.ts
📝 Walkthrough

Walkthrough

Adds hosted WorkOS auth across the repo: new typed contracts and facade methods, large refactor of auth-workos core (request normalization, profile/session shaping, mapper-driven identity sync), config/type normalization with provider selection, framework route handlers, UI links/forms, CLI scaffold changes, docs, and tests.

Changes

WorkOS Authentication Integration

Layer / File(s) Summary
Type Contracts
packages/auth-workos/src/contracts.ts
Adds WorkosJsonValue, tightens WorkosIdentityProfile, adds WorkosLogoutResult and WorkosCompleteAuthResult, and extends WorkosAuthFacade with login/register/logout/complete methods.
Core Auth Library
packages/auth-workos/src/index.ts
Refactors to accept flexible WorkosRequestInput, normalizes headers/URL, adds JWT claim helpers, freezes/canonicalizes WorkOS profiles, creates WorkOS session payloads, mapper-driven create/update inputs, and implements login/register/complete/logout flows.
Config Types & Defaults
packages/config/src/types.ts, packages/config/src/defaults.ts
Removes cookiePassword/sessionCookie from user inputs, introduces HoloAuthWorkosConfig wrapper with optional provider, normalizes WorkOS config with default wos-session cookie, and lets normalizeAuthConfig accept appKey for socialEncryptionKey fallback.
CLI Scaffolding
packages/cli/src/project/scaffold/*
Stop emitting AUTH_SOCIAL_ENCRYPTION_KEY, remove WorkOS cookie/password envs, add AUTH_WORKOS_PROVIDER, and refine provider-detection logic.
Blog App Configs
apps/blog-{next,nuxt,sveltekit}/config/auth.ts
Add personalAccessTokens: { defaultAbilities: [] }, remove socialEncryptionKey, add workos.provider env selection, simplify workos.dashboard to client credentials/redirectUri.
Route Handlers (entry points)
apps/blog-*/app/api/auth/workos/*, apps/blog-nuxt/server/api/auth/workos/*, apps/blog-sveltekit/src/routes/api/auth/workos/*
Add login/register endpoints delegating to facade, callback endpoints calling completeWorkosAuth and redirecting on failure to /login?error=... or to /admin on success, and logout endpoints calling logoutWithWorkos (JSON 422 on failure or 303 redirect on success).
Login/Register UI
apps/blog-*/app/*/login, apps/blog-*/app/*/register, apps/blog-sveltekit/src/routes/*
Add "Continue with WorkOS" and "Register with WorkOS" links pointing to /api/auth/workos/login and /api/auth/workos/register alongside existing social options.
Logout Forms
apps/blog-next/app/auth-nav.tsx, apps/blog-nuxt/app/app.vue, apps/blog-sveltekit/src/routes/+layout.svelte
Add inline POST forms to /api/auth/workos/logout with "Logout from WorkOS" button and .logout-form { display: inline } for header layout.
Runtime Cookie Handling
packages/auth/src/runtime/responseCookies.ts
Widen config.workos typing to allow config/string/undefined, add isHostedProviderConfig guard, and filter hosted session cookie extraction by type.
Core Detection & Loader
packages/core/src/portable/holo.ts, packages/config/src/loader.ts
Detect WorkOS providers by scanning non-provider object entries; pass app.key into normalizeAuthConfig.
Tests & Fixtures
packages/auth-workos/tests/*, packages/auth/tests/*, packages/config/tests/*, packages/cli/tests/*, tests/example-app-auth-flow.mjs, apps/*/tests/*
Extend fixtures (profilePictureUrl, typed metadata/raw, timezone), add helpers (completeWorkosSessionFixture, createUnsignedJwt), update tests to expect provider discriminator and normalized session cookie, and update example app flow assertions for WorkOS UI and callback failure redirect.
Documentation
apps/docs/docs/auth/workos.md
Rewrite describing hosted AuthKit flow with concrete login/register/callback/logout route examples, Nuxt server variant, session lifetime alignment note, and mapping/local field guidance.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

🐰 I hopped through routes and types so neat,
Frosted JSON fields and synced each seat,
From login link to logout's gentle art,
WorkOS and Holo now play their part.

🚥 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 'add workos logic' is vague and generic, lacking specificity about the scope and nature of the changes. It doesn't clearly convey the extent of the implementation (new authentication flows, configuration changes, API route handlers, and documentation updates across multiple frameworks). Consider using a more descriptive title that captures the main intent, such as 'Implement WorkOS authentication integration' or 'Add WorkOS auth flow with login, register, callback, and logout handlers'.
✅ 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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-model-naming

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

🧹 Nitpick comments (12)
tests/example-app-auth-flow.mjs (1)

293-295: ⚡ Quick win

Also assert the callback redirect includes an error query param.

Right now this only validates /login path, so a redirect without error context would still pass.

🔧 Suggested test tightening
-  assertRedirectsTo(await fetchAuthText('/api/auth/workos/callback', {
-    allowFailure: true,
-  }), '/login')
+  const workosCallback = await fetchAuthText('/api/auth/workos/callback', {
+    allowFailure: true,
+  })
+  assertRedirectsTo(workosCallback, '/login')
+  const workosCallbackLocation = workosCallback.response.headers.get('location')
+  assert.ok(workosCallbackLocation, 'Expected WorkOS callback redirect to include a location header.')
+  const workosCallbackUrl = new URL(workosCallbackLocation, workosCallback.response.url)
+  assert.ok(
+    workosCallbackUrl.searchParams.get('error'),
+    'Expected WorkOS callback failure redirect to include an error query parameter.',
+  )
🤖 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 `@tests/example-app-auth-flow.mjs` around lines 293 - 295, The test currently
only checks the path via assertRedirectsTo(await
fetchAuthText('/api/auth/workos/callback', { allowFailure: true }), '/login');
update it to also assert the redirect includes an error query param: either
tighten the expected redirect to include an error query (e.g.
'/login?error=...') or fetch the Location/redirect URL from fetchAuthText's
response and assert that new URL includes an "error" query key; look for and
update the call to assertRedirectsTo and/or add a follow-up assertion after
fetchAuthText('/api/auth/workos/callback', ...) to verify the presence of the
error query param.
packages/auth-workos/src/index.ts (6)

1107-1116: ⚡ Quick win

getStringField checks value.trim() but returns the untrimmed value.

When a WorkOS response includes a padded value like " org_123 ", this helper returns it verbatim because value.trim() is only used as a truthiness guard. That untrimmed value then flows into sessionId, accessToken, and organizationId derivation in authenticateWorkosCode. Returning the trimmed value would keep the rest of the pipeline working with clean strings.

♻️ Suggested fix
 function getStringField(input: Readonly<Record<string, unknown>>, ...names: readonly string[]): string | undefined {
   for (const name of names) {
     const value = input[name]
-    if (typeof value === 'string' && value.trim()) {
-      return value
-    }
+    if (typeof value === 'string' && value.trim()) {
+      return value.trim()
+    }
   }

   return 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 `@packages/auth-workos/src/index.ts` around lines 1107 - 1116, getStringField
currently uses value.trim() only as a truthiness check but returns the original
untrimmed string, causing padded values to propagate into sessionId,
accessToken, and organizationId in authenticateWorkosCode; change getStringField
(the function in packages/auth-workos/src/index.ts) to return the trimmed string
(value.trim()) when returning a string so callers receive cleaned values,
preserving the existing truthiness logic and types.

1528-1530: ⚡ Quick win

Module-level caches survive resetWorkosAuthRuntime.

workosDefaultProviderRuntimeCache (Line 125) and workosJwksCache (Line 126) are populated lazily on first verification and are never cleared by resetWorkosAuthRuntime. In long-running tests or hot-reload scenarios where the same clientId/apiKey are re-used after a reset but the underlying keys/runtimes change, this can serve stale WorkosProviderRuntime or stale JWKS. Consider clearing both caches inside resetWorkosAuthRuntime so reset truly returns the module to a pristine state.

♻️ Suggested fix
 export function resetWorkosAuthRuntime(): void {
   getRuntimeState().bindings = undefined
+  workosDefaultProviderRuntimeCache.clear()
+  workosJwksCache.clear()
 }
🤖 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-workos/src/index.ts` around lines 1528 - 1530,
resetWorkosAuthRuntime currently only clears getRuntimeState().bindings but
leaves module-level caches populated; modify resetWorkosAuthRuntime to also
clear the module-level caches workosDefaultProviderRuntimeCache and
workosJwksCache (e.g., call .clear() or reassign to a fresh Map/undefined as
appropriate) so that any cached WorkosProviderRuntime or JWKS entries are
removed and the module returns to a pristine state.

446-497: 💤 Low value

email is not trimmed before being used as identity output.

normalizeWorkosUserProfile reads user.email as-is (Line 460) and stores it on the frozen profile. Downstream consumers (resolveEmailForCreation, syncIdentity, ensureNoUnexpectedEmailCollision) re-trim it everywhere, so the data isn't broken — but it leaves the canonical profile holding values like ' user@app.test ' if a provider ever returns padded strings. Normalizing once at the boundary (similar to how firstName/lastName are passed through) would centralize the contract and let the rest of the module stop defensively trimming.

🤖 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-workos/src/index.ts` around lines 446 - 497, The profile
builder normalizeWorkosUserProfile currently assigns email directly from
user.email which can contain leading/trailing whitespace; update it to trim the
value once at the boundary (e.g., compute const email = typeof user.email ===
'string' ? user.email.trim() : '') and then use that trimmed email everywhere in
the returned WorkosIdentityProfile (including name fallback logic) so downstream
functions can rely on a canonical, trimmed email.

134-142: 💤 Low value

Defining the runtime state with configurable: false and unwritable value blocks legitimate cleanup.

Object.defineProperty here leaves writable and configurable both at their non-strict defaults of false, so once the property is set on globalThis it cannot be redefined or deleted, and the slot cannot be reassigned. The current code only mutates state.bindings, so it works, but any future attempt to swap the state object (e.g., to share with another module instance during HMR or test isolation) will throw. Either set writable: true, configurable: true or just use a plain assignment to the symbol-like key — the privacy benefit of enumerable: false alone is sufficient.

♻️ Suggested fix
   if (!runtimeGlobal[WORKOS_RUNTIME_STATE_KEY]) {
     Object.defineProperty(runtimeGlobal, WORKOS_RUNTIME_STATE_KEY, {
       value: {},
       enumerable: false,
-      configurable: false,
+      configurable: true,
+      writable: true,
     })
   }
🤖 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-workos/src/index.ts` around lines 134 - 142, The property
defined on runtimeGlobal using Object.defineProperty with
WORKOS_RUNTIME_STATE_KEY is non-configurable and non-writable by default,
preventing redefinition or deletion; update the definition in the module that
creates the runtime state (the code around runtimeGlobal and
WORKOS_RUNTIME_STATE_KEY, likely in the get runtime initializer) to either (a)
set writable: true and configurable: true along with enumerable: false, or (b)
replace the Object.defineProperty call with a plain assignment
runtimeGlobal[WORKOS_RUNTIME_STATE_KEY] = {} while keeping enumerable: false
behavior via a Symbol-style key; reference WORKOS_RUNTIME_STATE_KEY and
runtimeGlobal to locate the change and ensure subsequent code that mutates
state.bindings continues to work.

1417-1457: ⚡ Quick win

getAuthRuntime().guard(guard).user() is awaited but its result is unused.

Inside logoutWithWorkos, Line 1433 awaits .user() immediately before reading the WorkOS session payload, yet the returned user is never consumed. If the intent is just to hydrate bindings.context/load the session, that side effect is already covered by setSessionId on Line 1431 and the direct bindings.session.read inside readCurrentWorkosLogoutSession. If .user() is intentional (e.g., to surface auth/session errors before logout), a comment would help; otherwise the call can be dropped to avoid the extra adapter round-trip and the risk of mapping a non-WorkOS session error to workos_logout_failed instead of workos_session_missing.

🤖 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-workos/src/index.ts` around lines 1417 - 1457, The await of
getAuthRuntime().guard(guard).user() inside logoutWithWorkos is unused and
causes an unnecessary adapter round-trip and potential misleading error mapping;
remove that call (the await getAuthRuntime().guard(guard).user() line) unless
you intentionally want to surface session/auth errors—if you do, instead keep it
but wrap it in its own try/catch that returns a distinct error code (or adds a
comment) so readCurrentWorkosLogoutSession and setSessionId remain the primary
session checks (references: logoutWithWorkos,
getAuthRuntime().guard(guard).user(), setSessionId,
readCurrentWorkosLogoutSession).

564-596: 💤 Low value

bindings.config.workos.provider access still relies on the loose index signature.

Because NormalizedHoloAuthWorkosConfig widens the provider selector to NormalizedAuthWorkosProviderConfig | string | undefined, the typeof === 'string' check on Line 571 is genuinely necessary at the type level, but it also means readers can't tell at a glance whether the selector ever overlaps with provider keys. If a provider entry is ever named provider, getWorkosProviderEntries in packages/config/src/defaults.ts filters it out, but the runtime here would still resolve 'provider' as a valid name later via the configured-provider branches. Worth either documenting the reserved key or rejecting provider as a provider name during normalization to keep the two layers consistent.

🤖 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-workos/src/index.ts` around lines 564 - 596, The runtime
ambiguity comes from allowing a provider entry named "provider" in
bindings.config.workos while resolveConfiguredProviderName also reads
bindings.config.workos.provider; fix this by reserving the "provider" key at
normalization time: update the NormalizedHoloAuthWorkosConfig creation (and
getWorkosProviderEntries in packages/config/src/defaults.ts) to either filter
out or throw on any provider entry whose key === 'provider' so provider names
cannot collide, and then keep resolveConfiguredProviderName as-is (it can safely
treat bindings.config.workos.provider as the reserved default string); ensure
tests/validation reflect the new reserved-key rule.
packages/auth-workos/tests/package.test.ts (1)

858-934: 💤 Low value

Body assertion relies on JSON.stringify dropping undefined fields.

The hosted callback fetch mock asserts init?.body equals a JSON payload containing only grant_type, client_id, client_secret, and code. The runtime in authenticateWorkosCode also adds ip_address and user_agent, which only get dropped from the serialized body because the test Request has no x-forwarded-for/x-real-ip/user-agent headers, leaving those fields undefined. This makes the assertion brittle — adding default headers anywhere upstream (e.g., a test util setting user-agent) would break this exact-string match without any behavioral regression. Consider parsing the JSON and using toMatchObject (or expect.objectContaining) for the four fields you actually care about.

🤖 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-workos/tests/package.test.ts` around lines 858 - 934, The
test's fetch mock currently asserts init?.body equals a specific JSON string,
which is brittle because authenticateWorkosCode may include undefined
ip_address/user_agent fields; update the assertion to parse the body
(JSON.parse(String(init?.body))) and assert only the required keys using
toMatchObject or expect.objectContaining for { grant_type: 'authorization_code',
client_id: 'workos-client', client_secret: 'workos-key', code: 'code_123' } —
change the assertion inside the vi.stubGlobal fetch in the "completes the hosted
WorkOS callback with typed user mapping" test so it verifies the parsed body
contains those fields rather than matching the exact JSON string.
packages/auth-workos/src/contracts.ts (1)

117-122: ⚡ Quick win

WorkosAuthFacade.completeWorkosAuth return type is much looser than the implementation.

The implementation in packages/auth-workos/src/index.ts returns a precise discriminated union (frozen objects with ok: true carrying provider, guard, authProvider, status, user, identity, session, authSession, or ok: false with code/message). The facade contract here erases all of that to { ok: boolean; code?: string; message?: string; user?: AuthUserLike }, so consumers typed against the facade lose access to identity, authSession, session, status, etc., and cannot narrow on ok. Mirroring the implementation’s discriminated union (or exporting it as a named type and referencing it here) would prevent the facade from being a typing downgrade for callers.

🤖 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-workos/src/contracts.ts` around lines 117 - 122, The public
contract for WorkosAuthFacade.completeWorkosAuth is too permissive and loses the
implementation's discriminated union; update the return type to mirror the
precise frozen discriminated union used in packages/auth-workos/src/index.ts (or
export that union as a named type and reference it here) so callers can narrow
on ok and access
provider/guard/authProvider/status/identity/session/authSession/user;
specifically change the signature of WorkosAuthFacade.completeWorkosAuth to
return that exported union type instead of the current loose { ok: boolean;
code?: string; message?: string; user?: AuthUserLike } shape.
packages/config/src/defaults.ts (2)

1505-1535: ⚡ Quick win

Variable shadowing in normalizeWorkosConfig makes the logic harder to follow.

The outer provider on Line 1511 (the selected provider name) is shadowed by the destructured provider parameter (an AuthWorkosProviderConfig) inside the .map callbacks on Lines 1520 and 1530. While the outer reference on Line 1524 still resolves correctly because the inner provider is parameter-scoped, the dual meaning of provider (selector name vs. provider config object) within the same function is easy to misread.

♻️ Suggested rename
-  const providerEntries = getWorkosProviderEntries(config)
-  const provider = config?.provider?.trim() || undefined
+  const providerEntries = getWorkosProviderEntries(config)
+  const selectedProvider = config?.provider?.trim() || undefined
   if (providerEntries.length === 0) {
-    if (provider) {
-      throw new Error(`[Holo Auth] WorkOS provider "${provider}" is not configured.`)
+    if (selectedProvider) {
+      throw new Error(`[Holo Auth] WorkOS provider "${selectedProvider}" is not configured.`)
     }

     return holoAuthDefaults.workos
   }

-  const normalizedEntries = providerEntries.map(([name, provider]) => {
+  const normalizedEntries = providerEntries.map(([name, providerConfig]) => {
     const normalizedName = normalizeConnectionName(name, 'Auth WorkOS provider name')
-    return [normalizedName, provider] as const
+    return [normalizedName, providerConfig] as const
   })
-  if (provider && !normalizedEntries.some(([name]) => name === provider)) {
-    throw new Error(`[Holo Auth] WorkOS provider "${provider}" is not configured.`)
+  if (selectedProvider && !normalizedEntries.some(([name]) => name === selectedProvider)) {
+    throw new Error(`[Holo Auth] WorkOS provider "${selectedProvider}" is not configured.`)
   }

   return Object.freeze({
-    ...(typeof provider === 'undefined' ? {} : { provider }),
-    ...Object.fromEntries(normalizedEntries.map(([name, provider]) => [
+    ...(typeof selectedProvider === 'undefined' ? {} : { provider: selectedProvider }),
+    ...Object.fromEntries(normalizedEntries.map(([name, providerConfig]) => [
       name,
-      normalizeWorkosProvider(name, provider, guards, providers),
+      normalizeWorkosProvider(name, providerConfig, guards, providers),
     ])),
   })
🤖 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/config/src/defaults.ts` around lines 1505 - 1535, The function
normalizeWorkosConfig uses an outer variable named provider (selected provider
name) and also destructures a provider parameter inside the providerEntries.map
callbacks, causing shadowing and confusion; rename the inner mapped variable
(e.g., to providerConfig or entryProvider) in the providerEntries.map that
builds normalizedEntries and in the Object.fromEntries mapping so that calls to
normalizeWorkosProvider use the new name (normalizeWorkosProvider(name,
providerConfig, guards, providers)), and ensure all references in those
callbacks are updated accordingly to avoid shadowing while preserving the same
behavior.

1480-1503: 💤 Low value

Type guard rejects string entries that the HoloAuthWorkosConfig type explicitly permits.

HoloAuthWorkosConfig declares readonly [provider: string]: AuthWorkosProviderConfig | string | undefined, but isWorkosProviderConfig returns false for strings and getWorkosProviderEntries then throws "WorkOS provider \"${name}\" must be an object.". If string values are intentionally not supported in normalized config, consider tightening the type (e.g., dropping | string from the index signature in types.ts) so the contract matches the runtime behavior; otherwise, this rejection is a behavior gap. Either way, the error message could mention the allowed shapes more accurately.

🤖 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/config/src/defaults.ts` around lines 1480 - 1503, The type guard
isWorkosProviderConfig currently rejects string entries while
HoloAuthWorkosConfig permits them, causing getWorkosProviderEntries to throw
incorrectly; either update the runtime to accept strings (e.g., change
isWorkosProviderConfig to treat typeof value === 'object' || typeof value ===
'string' and in getWorkosProviderEntries normalize string shorthand into an
AuthWorkosProviderConfig object before pushing) or tighten the static type
(remove | string from HoloAuthWorkosConfig) so types and runtime align, and
update the thrown Error message in getWorkosProviderEntries to list the allowed
shapes (object or string) when rejecting values.
packages/config/src/types.ts (1)

628-707: ⚖️ Poor tradeoff

Index signature widening loses provider-config typing for known WorkOS entries.

Both HoloAuthWorkosConfig and NormalizedHoloAuthWorkosConfig use [provider: string]: AuthWorkosProviderConfig | string | undefined so the provider?: string selector is structurally compatible. As a side effect, every provider lookup (e.g., config.workos.dashboard) now widens to AuthWorkosProviderConfig | string | undefined, forcing consumers (like getConfiguredProviderConfig in packages/auth-workos/src/index.ts) to defensively narrow with typeof configured !== 'object' even though the runtime never accepts string entries. If string values aren't actually supported, dropping | string and keeping only AuthWorkosProviderConfig | undefined would let TS reject provider: string cleanly via a discriminated wrapper such as:

export type HoloAuthWorkosConfig = {
  readonly provider?: string
} & {
  readonly [provider: string]: AuthWorkosProviderConfig | undefined
}

(or move the selector into a nested key) and remove the runtime guard duplication.

🤖 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/config/src/types.ts` around lines 628 - 707, The index signature on
HoloAuthWorkosConfig and NormalizedHoloAuthWorkosConfig is too permissive (it
allows string values) which widens lookups like config.workos.dashboard to
AuthWorkosProviderConfig | string | undefined; change both types to an
intersection style (keep readonly provider?: string but make the indexer
readonly [provider: string]: AuthWorkosProviderConfig | undefined and similarly
for NormalizedAuthWorkosConfig with NormalizedAuthWorkosProviderConfig) so
string entries are disallowed, and then remove the defensive runtime/type guard
in getConfiguredProviderConfig (packages/auth-workos/src/index.ts) that checks
typeof configured !== 'object' because the type now enforces object-valued
providers.
🤖 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/auth/workos/callback.get.ts`:
- Around line 6-8: The redirect currently builds a query with
encodeURIComponent(result.code) which can produce error=undefined when
result.code is absent; update the callback error path (the branch that calls
sendRedirect) to use a stable fallback (e.g., const errCode = result.code ??
'unknown_error') before encoding and pass encodeURIComponent(errCode) to
sendRedirect so the URL always contains a meaningful error code; locate this in
the callback handler where result.ok is checked and modify the redirect
construction accordingly.

In `@packages/auth-workos/tests/package.test.ts`:
- Around line 74-77: The derived name assignment uses the expression
[session.identity.firstName, session.identity.lastName].filter(Boolean).join('
') which returns '' when both names are missing, preventing the following
nullish coalescing from running; change the middle operand so an empty join
becomes undefined/null (e.g., compute the joined string and return undefined if
it's empty) so the chain session.identity.name ?? <joined-or-undefined> ??
session.identity.email ?? session.identity.id properly falls through; update the
expression around the variable name (the join of firstName/lastName) to convert
'' to undefined.

In `@packages/auth/src/runtime/responseCookies.ts`:
- Around line 49-51: The current type guard in the for loop over
Object.values(config.workos) falsely narrows any non-null object to
HostedProviderConfig; change the runtime check to ensure the required property
exists before treating it as HostedProviderConfig—e.g., in the loop condition
verify ("sessionCookie" in value && typeof (value as any).sessionCookie ===
"string") or call a small predicate isHostedProviderConfig(value) that returns
true only when value is an object and has a string sessionCookie; update the
loop to use that predicate (references: config.workos, HostedProviderConfig,
provider.sessionCookie) so undefined sessionCookie values cannot be added to the
Set.

---

Nitpick comments:
In `@packages/auth-workos/src/contracts.ts`:
- Around line 117-122: The public contract for
WorkosAuthFacade.completeWorkosAuth is too permissive and loses the
implementation's discriminated union; update the return type to mirror the
precise frozen discriminated union used in packages/auth-workos/src/index.ts (or
export that union as a named type and reference it here) so callers can narrow
on ok and access
provider/guard/authProvider/status/identity/session/authSession/user;
specifically change the signature of WorkosAuthFacade.completeWorkosAuth to
return that exported union type instead of the current loose { ok: boolean;
code?: string; message?: string; user?: AuthUserLike } shape.

In `@packages/auth-workos/src/index.ts`:
- Around line 1107-1116: getStringField currently uses value.trim() only as a
truthiness check but returns the original untrimmed string, causing padded
values to propagate into sessionId, accessToken, and organizationId in
authenticateWorkosCode; change getStringField (the function in
packages/auth-workos/src/index.ts) to return the trimmed string (value.trim())
when returning a string so callers receive cleaned values, preserving the
existing truthiness logic and types.
- Around line 1528-1530: resetWorkosAuthRuntime currently only clears
getRuntimeState().bindings but leaves module-level caches populated; modify
resetWorkosAuthRuntime to also clear the module-level caches
workosDefaultProviderRuntimeCache and workosJwksCache (e.g., call .clear() or
reassign to a fresh Map/undefined as appropriate) so that any cached
WorkosProviderRuntime or JWKS entries are removed and the module returns to a
pristine state.
- Around line 446-497: The profile builder normalizeWorkosUserProfile currently
assigns email directly from user.email which can contain leading/trailing
whitespace; update it to trim the value once at the boundary (e.g., compute
const email = typeof user.email === 'string' ? user.email.trim() : '') and then
use that trimmed email everywhere in the returned WorkosIdentityProfile
(including name fallback logic) so downstream functions can rely on a canonical,
trimmed email.
- Around line 134-142: The property defined on runtimeGlobal using
Object.defineProperty with WORKOS_RUNTIME_STATE_KEY is non-configurable and
non-writable by default, preventing redefinition or deletion; update the
definition in the module that creates the runtime state (the code around
runtimeGlobal and WORKOS_RUNTIME_STATE_KEY, likely in the get runtime
initializer) to either (a) set writable: true and configurable: true along with
enumerable: false, or (b) replace the Object.defineProperty call with a plain
assignment runtimeGlobal[WORKOS_RUNTIME_STATE_KEY] = {} while keeping
enumerable: false behavior via a Symbol-style key; reference
WORKOS_RUNTIME_STATE_KEY and runtimeGlobal to locate the change and ensure
subsequent code that mutates state.bindings continues to work.
- Around line 1417-1457: The await of getAuthRuntime().guard(guard).user()
inside logoutWithWorkos is unused and causes an unnecessary adapter round-trip
and potential misleading error mapping; remove that call (the await
getAuthRuntime().guard(guard).user() line) unless you intentionally want to
surface session/auth errors—if you do, instead keep it but wrap it in its own
try/catch that returns a distinct error code (or adds a comment) so
readCurrentWorkosLogoutSession and setSessionId remain the primary session
checks (references: logoutWithWorkos, getAuthRuntime().guard(guard).user(),
setSessionId, readCurrentWorkosLogoutSession).
- Around line 564-596: The runtime ambiguity comes from allowing a provider
entry named "provider" in bindings.config.workos while
resolveConfiguredProviderName also reads bindings.config.workos.provider; fix
this by reserving the "provider" key at normalization time: update the
NormalizedHoloAuthWorkosConfig creation (and getWorkosProviderEntries in
packages/config/src/defaults.ts) to either filter out or throw on any provider
entry whose key === 'provider' so provider names cannot collide, and then keep
resolveConfiguredProviderName as-is (it can safely treat
bindings.config.workos.provider as the reserved default string); ensure
tests/validation reflect the new reserved-key rule.

In `@packages/auth-workos/tests/package.test.ts`:
- Around line 858-934: The test's fetch mock currently asserts init?.body equals
a specific JSON string, which is brittle because authenticateWorkosCode may
include undefined ip_address/user_agent fields; update the assertion to parse
the body (JSON.parse(String(init?.body))) and assert only the required keys
using toMatchObject or expect.objectContaining for { grant_type:
'authorization_code', client_id: 'workos-client', client_secret: 'workos-key',
code: 'code_123' } — change the assertion inside the vi.stubGlobal fetch in the
"completes the hosted WorkOS callback with typed user mapping" test so it
verifies the parsed body contains those fields rather than matching the exact
JSON string.

In `@packages/config/src/defaults.ts`:
- Around line 1505-1535: The function normalizeWorkosConfig uses an outer
variable named provider (selected provider name) and also destructures a
provider parameter inside the providerEntries.map callbacks, causing shadowing
and confusion; rename the inner mapped variable (e.g., to providerConfig or
entryProvider) in the providerEntries.map that builds normalizedEntries and in
the Object.fromEntries mapping so that calls to normalizeWorkosProvider use the
new name (normalizeWorkosProvider(name, providerConfig, guards, providers)), and
ensure all references in those callbacks are updated accordingly to avoid
shadowing while preserving the same behavior.
- Around line 1480-1503: The type guard isWorkosProviderConfig currently rejects
string entries while HoloAuthWorkosConfig permits them, causing
getWorkosProviderEntries to throw incorrectly; either update the runtime to
accept strings (e.g., change isWorkosProviderConfig to treat typeof value ===
'object' || typeof value === 'string' and in getWorkosProviderEntries normalize
string shorthand into an AuthWorkosProviderConfig object before pushing) or
tighten the static type (remove | string from HoloAuthWorkosConfig) so types and
runtime align, and update the thrown Error message in getWorkosProviderEntries
to list the allowed shapes (object or string) when rejecting values.

In `@packages/config/src/types.ts`:
- Around line 628-707: The index signature on HoloAuthWorkosConfig and
NormalizedHoloAuthWorkosConfig is too permissive (it allows string values) which
widens lookups like config.workos.dashboard to AuthWorkosProviderConfig | string
| undefined; change both types to an intersection style (keep readonly
provider?: string but make the indexer readonly [provider: string]:
AuthWorkosProviderConfig | undefined and similarly for
NormalizedAuthWorkosConfig with NormalizedAuthWorkosProviderConfig) so string
entries are disallowed, and then remove the defensive runtime/type guard in
getConfiguredProviderConfig (packages/auth-workos/src/index.ts) that checks
typeof configured !== 'object' because the type now enforces object-valued
providers.

In `@tests/example-app-auth-flow.mjs`:
- Around line 293-295: The test currently only checks the path via
assertRedirectsTo(await fetchAuthText('/api/auth/workos/callback', {
allowFailure: true }), '/login'); update it to also assert the redirect includes
an error query param: either tighten the expected redirect to include an error
query (e.g. '/login?error=...') or fetch the Location/redirect URL from
fetchAuthText's response and assert that new URL includes an "error" query key;
look for and update the call to assertRedirectsTo and/or add a follow-up
assertion after fetchAuthText('/api/auth/workos/callback', ...) to verify the
presence of the error query param.
🪄 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: bb0d5588-615e-4650-a4f2-099d3e924e58

📥 Commits

Reviewing files that changed from the base of the PR and between bfe67b3 and aabc7f6.

📒 Files selected for processing (43)
  • apps/blog-next/app/api/auth/workos/callback/route.ts
  • apps/blog-next/app/api/auth/workos/login/route.ts
  • apps/blog-next/app/api/auth/workos/logout/route.ts
  • apps/blog-next/app/api/auth/workos/register/route.ts
  • apps/blog-next/app/auth-nav.tsx
  • apps/blog-next/app/login/page.tsx
  • apps/blog-next/app/register/page.tsx
  • apps/blog-next/config/auth.ts
  • apps/blog-nuxt/app/app.vue
  • apps/blog-nuxt/app/pages/login/index.vue
  • apps/blog-nuxt/app/pages/register/index.vue
  • apps/blog-nuxt/config/auth.ts
  • apps/blog-nuxt/server/api/auth/workos/callback.get.ts
  • apps/blog-nuxt/server/api/auth/workos/login.get.ts
  • apps/blog-nuxt/server/api/auth/workos/logout.post.ts
  • apps/blog-nuxt/server/api/auth/workos/register.get.ts
  • apps/blog-sveltekit/config/auth.ts
  • apps/blog-sveltekit/src/routes/+layout.svelte
  • apps/blog-sveltekit/src/routes/api/auth/workos/callback/+server.ts
  • apps/blog-sveltekit/src/routes/api/auth/workos/login/+server.ts
  • apps/blog-sveltekit/src/routes/api/auth/workos/logout/+server.ts
  • apps/blog-sveltekit/src/routes/api/auth/workos/register/+server.ts
  • apps/blog-sveltekit/src/routes/login/+page.svelte
  • apps/blog-sveltekit/src/routes/register/+page.svelte
  • apps/docs/docs/auth/workos.md
  • packages/auth-workos/src/contracts.ts
  • packages/auth-workos/src/index.ts
  • packages/auth-workos/tests/package.test.ts
  • packages/auth/src/runtime/responseCookies.ts
  • packages/auth/tests/docs-smoke.test.ts
  • packages/auth/tests/package.test.ts
  • packages/cli/src/project/scaffold/config-renderers.ts
  • packages/cli/src/project/scaffold/dependencies.ts
  • packages/cli/src/project/scaffold/project-renderers.ts
  • packages/cli/tests/cli.test.ts
  • packages/config/src/defaults.ts
  • packages/config/src/loader.ts
  • packages/config/src/types.ts
  • packages/config/tests/config.test.ts
  • packages/config/tests/config.type.test.ts
  • packages/core/src/portable/holo.ts
  • packages/core/tests/auth-runtime.test.ts
  • tests/example-app-auth-flow.mjs

Comment thread apps/blog-nuxt/server/api/auth/workos/callback.get.ts
Comment thread packages/auth-workos/tests/package.test.ts
Comment thread packages/auth/src/runtime/responseCookies.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

🤖 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 `@packages/auth-workos/src/contracts.ts`:
- Around line 41-57: The two related result types disagree on whether
authSession is optional: WorkosCompleteAuthResult currently has authSession
required while WorkosAuthenticationResult declares authSession?:
AuthEstablishedSession; decide whether authSession can be absent and then make
the optionality consistent across both types (either make authSession optional
in WorkosCompleteAuthResult or required in WorkosAuthenticationResult) by
updating the type signatures for WorkosCompleteAuthResult and
WorkosAuthenticationResult (and any related readonly branches) so both reference
authSession with the same presence and the AuthEstablishedSession type.
🪄 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: 8b4babd8-809b-48d1-b70f-01b666272328

📥 Commits

Reviewing files that changed from the base of the PR and between aabc7f6 and aa42580.

📒 Files selected for processing (12)
  • apps/blog-next/tests/run.mjs
  • apps/blog-nuxt/server/api/auth/workos/callback.get.ts
  • apps/blog-nuxt/tests/run.mjs
  • apps/blog-sveltekit/tests/run.mjs
  • packages/auth-workos/src/contracts.ts
  • packages/auth-workos/src/index.ts
  • packages/auth-workos/tests/package.test.ts
  • packages/auth/src/runtime/responseCookies.ts
  • packages/auth/tests/package.test.ts
  • packages/config/src/defaults.ts
  • packages/config/tests/config.test.ts
  • tests/example-app-auth-flow.mjs
🚧 Files skipped from review as they are similar to previous changes (7)
  • tests/example-app-auth-flow.mjs
  • packages/auth/src/runtime/responseCookies.ts
  • packages/config/src/defaults.ts
  • packages/config/tests/config.test.ts
  • packages/auth-workos/tests/package.test.ts
  • packages/auth/tests/package.test.ts
  • packages/auth-workos/src/index.ts

Comment thread packages/auth-workos/src/contracts.ts
@cobraprojects cobraprojects merged commit 10c1bf7 into main May 11, 2026
1 check passed
@cobraprojects cobraprojects deleted the fix-model-naming branch May 11, 2026 13:42
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