Skip to content

chore(examples): update demo user interfaces#145

Merged
halvaradop merged 13 commits into
masterfrom
chore/update-example-ui
Apr 20, 2026
Merged

chore(examples): update demo user interfaces#145
halvaradop merged 13 commits into
masterfrom
chore/update-example-ui

Conversation

@halvaradop
Copy link
Copy Markdown
Member

@halvaradop halvaradop commented Apr 18, 2026

Description

This pull request updates the demo integration examples with a redesigned UI and improved structure. The new design provides a clear overview of available demos and includes direct navigation to dedicated rendering strategies such as server-side rendering (SSR) and client-side rendering (CSR), depending on framework support.

The visual style remains consistent with the existing design philosophy, focusing on minimalism using lines, boxes, and simple layouts.

Additionally, due to the scope and size of related changes, part of the implementation was moved to a separate package and later merged into this PR to keep concerns modular and manageable.


Next.js Demo

The following images showcase the updated design introduced in this PR:

localhost_3000_ (14) ---

Key Changes

  • Added client-side and server-side authentication demo pages with OAuth provider integration
  • Added credentials-based sign-in with username/password support
  • Added profile editing functionality
  • Introduced multiple framework implementations (Next.js, Nuxt, React Router, TanStack)

Related PRs

Summary by CodeRabbit

Release Notes

  • New Features

    • Added demo pages for client-side and server-side authentication flows
    • Enabled username/password credentials sign-in alongside OAuth providers
    • Added profile editing capability for authenticated users
    • Improved avatar support for GitHub, GitLab, and Bitbucket user images
  • Changes

    • Redesigned landing page with reference sections linking to authentication demos
    • Enhanced button styling with secondary variant support
    • Updated component spacing and layout constraints
  • Documentation

    • Added installation and quick start guides to core packages

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 18, 2026

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

Project Deployment Actions Updated (UTC)
auth Ready Ready Preview, Comment Apr 20, 2026 8:56pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

Warning

Rate limit exceeded

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

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 16 minutes and 10 seconds.

⌛ 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: 0675d909-2bdd-4ba1-ae57-795a11228afd

📥 Commits

Reviewing files that changed from the base of the PR and between c77d28b and 28e25b5.

📒 Files selected for processing (17)
  • .github/workflows/ci.yaml
  • apps/nuxt/app/composables/useAuthClient.ts
  • apps/nuxt/app/pages/client.vue
  • apps/react-router/app/components/edit-profile.tsx
  • apps/react-router/app/routes/index.tsx
  • apps/tanstack-start/src/routeTree.gen.ts
  • apps/tanstack-start/src/routes/server.tsx
  • apps/tanstack-start/src/styles.css
  • configs/tsdown-config/tsdown.config.ts
  • packages/core/vitest.config.ts
  • packages/elysia/vitest.config.ts
  • packages/express/vitest.config.ts
  • packages/hono/vitest.config.ts
  • packages/jose/vitest.config.ts
  • packages/rate-limiter/vitest.config.ts
  • packages/react/vitest.config.ts
  • turbo.json
📝 Walkthrough

Walkthrough

This PR standardizes authentication API contracts across signIn, signInCredentials, signOut, and updateSession with unified error handling and conditional redirect semantics. Credential-based authorization is added via createSecretValue and authorize handlers. Client API function signatures are refactored to use option objects. New server/client authentication pages demonstrate SSR/CSR flows across frameworks. Remote image patterns for OAuth avatars are configured, and the EditProfile component with min-h-container utility class are introduced.

Changes

Cohort / File(s) Summary
Core Auth API Types & Implementation
packages/core/src/@types/api.ts, packages/core/src/api/signIn.ts, packages/core/src/api/credentials.ts, packages/core/src/api/getSession.ts, packages/core/src/api/signOut.ts, packages/core/src/api/updateSession.ts
Introduced new standardized API type definitions with unified error handling, redirect semantics, and response helpers (toResponse(), headers). Updated implementations to wrap calls in error handling, normalize error codes/messages via isAuthErrorWithCode, and support redirectTo parameter.
Core Client API
packages/core/src/client/client.ts
Refactored function signatures: signIn/signInCredentials/signOut/updateSession now use generic Options parameter objects instead of separate parameters. Updated return types to be conditionally never when redirect is enabled.
Core Auth Error Handling
packages/core/src/shared/errors.ts, packages/core/src/shared/identity.ts
Added isAuthErrorWithCode type guard for error normalization. Updated UserIdentity.email to use Zod's email() validator for stricter validation.
Core Type Reorganization
packages/core/src/@types/index.ts, packages/core/src/@types/session.ts
Moved API-related types to new dedicated api.ts module and re-exported from index. Removed AuthActionAPIReturn, FunctionAPIContext, and all auth action option/return types from session.ts.
Core Tests
packages/core/test/api/signOut.test.ts, packages/core/test/api/updateSession.test.ts, packages/core/test/api/signIn.test.ts, packages/core/test/actions/...
Added comprehensive test coverage for updated API surfaces. Updated existing test assertions to match new response shapes with error fields, redirect flags, and redirectURL values. Changed strict equality to partial matching where appropriate.
Core Documentation
packages/core/README.md, packages/core/CHANGELOG.md
Added Installation and Quick Start sections with example usage of createAuth and createAuthClient. Documented standardized API and client contracts.
Next.js Package API & Config
packages/next/src/lib/api.ts, packages/next/src/@types/api.ts, packages/next/tsdown.config.ts, packages/next/README.md
Refactored API helper functions to use unified option objects (signInCredentials, updateSession, signOut). Added conditional return types resolving to never for redirect scenarios. Updated tsdown config with platform: "node", fixedExtension: false, and refined dependency bundling.
Next.js App Router Pages
apps/nextjs/app-router/src/app/client/page.tsx, apps/nextjs/app-router/src/app/server/page.tsx, apps/nextjs/app-router/src/app/page.tsx
Added new /client page demonstrating client-side auth with useAuth() hook. Added new /server page demonstrating server-side auth with server actions. Updated index page from auth-focused to landing page with flow cards and navigation links.
Next.js App Router Components & Config
apps/nextjs/app-router/src/components/..., apps/nextjs/app-router/src/@types/props.ts, apps/nextjs/app-router/src/lib/auth.ts, apps/nextjs/app-router/src/lib/metadata.ts
Removed AuthClient and AuthServer components. Added new EditProfile component for profile updates. Added credential authorization handler to createAuth. Updated ButtonProps to include "secondary" variant. Configured remote image patterns for GitHub/GitLab/Bitbucket avatars. Updated page title metadata.
Next.js App Router Layout & Styling
apps/nextjs/app-router/src/app/layout.tsx, apps/nextjs/app-router/src/app/globals.css
Removed Footer component rendering. Dropped bg-black from body classes. Added .min-h-container utility class with viewport-relative height and top margin.
Next.js App Router Footer
apps/nextjs/app-router/src/components/header.tsx, apps/nextjs/app-router/src/components/footer.tsx
Updated max-width constraint from max-w-5xl to max-w-6xl in both header and footer.
Next.js Pages Router Pages
apps/nextjs/pages-router/src/pages/client/index.tsx, apps/nextjs/pages-router/src/pages/server/index.tsx, apps/nextjs/pages-router/src/pages/index.tsx
Added new /client page with useAuth() hook and client-side auth flows. Added new /server page with getServerSideProps loader and server-side auth flows. Updated index page to use flow array and removed getServerSideProps.
Next.js Pages Router Components & Config
apps/nextjs/pages-router/src/components/..., apps/nextjs/pages-router/src/@types/props.ts, apps/nextjs/pages-router/src/lib/auth.ts
Removed AuthClient component. Added EditProfile component. Updated ButtonProps variant type. Added credential authorize handler. Updated header/footer max-widths and layout. Removed button border radius styling.
Next.js Pages Router App & Docs
apps/nextjs/pages-router/src/pages/_app.tsx, apps/nextjs/pages-router/src/pages/_document.tsx, apps/nextjs/pages-router/src/styles/globals.css, apps/nextjs/pages-router/next.config.ts
Added Google fonts loading and wrapped app in font variable div. Removed title element from document head. Removed .box-auth grid styles; added .min-h-container utility. Configured remote image patterns for OAuth providers.
Nuxt Integration Pages & Components
apps/nuxt/app/pages/client.vue, apps/nuxt/app/pages/server.vue, apps/nuxt/app/pages/index.vue
Added new /client page with useAuthClient() for client-side auth. Added new /server page with initial session fetch and server-side auth. Updated index page from auth UI to landing page with flow cards.
Nuxt Components & Composables
apps/nuxt/app/components/edit-profile.vue, apps/nuxt/app/composables/useAuthClient.ts
Added EditProfile SFC component. Refactored useAuthClient to accept optional initialSession, compute derived status, add refresh() helper, implement signInCredentials and updateSession methods with proper refresh logic.
Nuxt Layout & Styling
apps/nuxt/app/layouts/default.vue, apps/nuxt/app/assets/css/tailwind.css, apps/nuxt/app/components/header.vue, apps/nuxt/app/components/footer.vue, apps/nuxt/app/components/ui/button/index.ts
Removed Footer from layout. Added .min-h-container utility class. Updated header/footer max-widths to max-w-6xl. Added target="_blank" and rel="noopener noreferrer" to external links. Removed rounded-md from button variants.
React Router Integration API & Config
packages/react-router/src/@types/index.ts, packages/react-router/src/lib/api.ts, packages/react-router/tsdown.config.ts, packages/react-router/README.md
Added conditional return types for signOut and updateSession. Refactored API helpers to use unified option objects with request and optional spread fields. Updated tsdown config with fixedExtension: false and refined dependency bundling. Added Installation and Quick Start sections.
React Router Pages & Routes
apps/react-router/app/routes/client/index.tsx, apps/react-router/app/routes/server/index.tsx, apps/react-router/app/routes/index.tsx, apps/react-router/app/routes.ts
Added /client route with client-side auth using useAuth(). Added /server route with server-side auth loader and form actions. Updated index route from session-dependent UI to landing page with flow array. Updated route configuration to include new server/client routes.
React Router Components & Layout
apps/react-router/app/components/..., apps/react-router/app/@types/props.ts, apps/react-router/app/lib/auth.ts
Removed AuthClient component. Added EditProfile component. Added credential authorize handler. Updated header/footer max-widths and button styling. Updated ButtonProps variant type. Updated routes/auth-layout to remove footer and session loader.
React Router App Setup
apps/react-router/app/routes/auth-layout.tsx, apps/react-router/app/app.css
Removed route loader fetching session. Removed footer from layout. Added .min-h-container utility class. Removed AuthProvider initialization of initialSession.
React Package Types & Hooks
packages/react/src/@types/types.ts, packages/react/src/context.tsx, packages/react/src/hooks.ts
Simplified AuthClientInstance and AuthReactContextValue to use indexed access types from underlying client. Updated signInCredentials to take single options object. Updated updateSession to take single options object. Removed UpdateSessionCallOptions. Updated hook signatures to match new client API.
React Package Tests & Docs
packages/react/test/hooks.test.tsx, packages/react/CHANGELOG.md, packages/react/README.md
Updated hook tests to use new options-based signatures for signInCredentials and updateSession. Removed skipRefresh test. Updated README with simplified setup and new quick start steps.
TanStack Start Pages & Routes
apps/tanstack-start/src/routes/client.tsx, apps/tanstack-start/src/routes/server.tsx, apps/tanstack-start/src/routes/index.tsx
Added /client route with client-side auth UI using useAuth(). Added /server route with async loader and server-rendered auth UI. Updated index route from session-dependent UI to landing page with flow array.
TanStack Start Components & Auth
apps/tanstack-start/src/components/..., apps/tanstack-start/src/lib/auth-server.ts, apps/tanstack-start/src/routeTree.gen.ts, apps/tanstack-start/src/routes/__root.tsx
Removed AuthClient and AuthServer components. Added EditProfile component. Updated Footer component with expanded max-width, external link handling, and removed MinimalFooter. Updated auth server functions to use success flag and support credentials/session updates. Updated route tree to include /client and /server routes.
TanStack Start Styling
apps/tanstack-start/src/styles.css
Removed tw-animate-css import and keyframe animations (fadeIn, slideDown). Restructured theme variables into :root and .dark blocks. Added .min-h-container utility class.
Astro Integration
apps/astro/src/lib/server.ts
Updated getSession to check session.success instead of session.authenticated. Simplified signOut to return raw response instead of parsing JSON.
Build Configuration
configs/tsdown-config/tsdown.config.ts
Enabled sourcemaps. Changed dependency bundling from skipNodeModulesBundle: true to onlyBundle: false.
Lock File Management
skills-lock.json
Removed entire skills-lock.json file contents including tsdown skill entry.

Sequence Diagram

sequenceDiagram
    participant User as User/Client
    participant Browser as Browser
    participant Server as Server/API
    participant Database as Session/Database

    User->>Browser: Submit credentials (username/password)
    Browser->>Server: POST /api/auth/signInCredentials
    Server->>Server: Validate credentials
    Server->>Server: Generate sub via createSecretValue
    Server->>Database: Store session with user identity
    Database-->>Server: Session created
    Server-->>Browser: Return success + redirectURL or signInURL
    
    alt Redirect Enabled (redirect: true)
        Browser->>Browser: Redirect to redirectURL
    else Manual Redirect (redirect: false)
        Browser->>Server: Check response.signInURL
        Browser->>Browser: Handle redirect programmatically
    end
    
    User->>Browser: Edit profile (username/email)
    Browser->>Server: POST updateSession with FormData
    Server->>Server: Extract username/email from FormData
    Server->>Database: Update session.user.name/email
    Database-->>Server: Session updated
    Server-->>Browser: Return success + optional redirectURL
    Browser->>Browser: Refresh session state
    
    User->>Browser: Click Sign Out
    Browser->>Server: POST signOut with CSRF token
    Server->>Database: Invalidate session token
    Database-->>Server: Session cleared
    Server-->>Browser: Return success + redirectURL
    Browser->>Browser: Redirect and clear auth state
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • The main PR implements the centralized createAuthClient API and unified option object signatures introduced in related PRs, with significant overlap in authentication flow refactoring across all framework integrations.
  • This PR updates the core signInCredentials, updateSession, and signOut API surfaces with standardized contracts that directly correspond to changes documented and implemented in related credential-auth and session-management PRs.
  • The credential-based authorization via createSecretValue and authorize handlers directly implements the credentials sign-in feature introduced in a related PR, with identical handler signatures and secret generation patterns across all applications.

Poem

🐰 Hops through auth flows, credentials in sight,
API contracts unified, standardized tight,
From client to server, the paths align,
Sign in, update, sign out—no more design!
With EditProfile ✨ and pages so bright. 🎉

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/update-example-ui

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

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (3)
apps/nuxt/app/components/header.vue (1)

24-45: ⚠️ Potential issue | 🟡 Minor

Inconsistent rel="noopener noreferrer" on external links.

Only the Discord anchor received rel="noopener noreferrer"; the Documentation (Line 24-30) and Repository (Line 31-37) anchors still use target="_blank" without it, which is the same security/performance concern the PR intends to address. Apply consistently.

Proposed fix
                 <a
                     href="https://aura-stack-auth.vercel.app/docs"
                     class="text-sm text-muted-foreground hover:text-white transition-colors"
                     target="_blank"
+                    rel="noopener noreferrer"
                 >
                     Documentation
                 </a>
                 <a
                     href="https://github.com/aura-stack-ts/auth"
                     class="text-sm text-muted-foreground hover:text-white transition-colors"
                     target="_blank"
+                    rel="noopener noreferrer"
                 >
                     Repository
                 </a>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nuxt/app/components/header.vue` around lines 24 - 45, The Documentation
and Repository anchor tags in header.vue open external links with
target="_blank" but lack rel="noopener noreferrer"; update the two anchors whose
inner text is "Documentation" and "Repository" to include rel="noopener
noreferrer" (matching the existing Discord anchor) to prevent window.opener
security issues and improve performance.
packages/next/src/lib/api.ts (1)

19-26: ⚠️ Potential issue | 🟠 Major

Preserve Set-Cookie attributes when syncing auth cookies.

applyCookies currently keeps only name=value, dropping HttpOnly, Secure, SameSite, Path, Expires, and Max-Age. That can weaken session-cookie security and can also make sign-out unreliable when the auth API sends expiry cookies.

Proposed safer parsing shape
 async function applyCookies(responseHeaders: Headers) {
     const cookieStore = await cookies()
     const setCookies = responseHeaders.getSetCookie()
     for (const cookieStr of setCookies) {
-        const [nameValue] = cookieStr.split(";").map((s) => s.trim())
-        const [name, value] = nameValue.split("=")
-        cookieStore.set(name, value, {})
+        const [nameValue, ...attributes] = cookieStr.split(";").map((s) => s.trim())
+        const separatorIndex = nameValue.indexOf("=")
+        if (separatorIndex === -1) continue
+
+        const name = nameValue.slice(0, separatorIndex)
+        const value = nameValue.slice(separatorIndex + 1)
+        const options = {} as {
+            path?: string
+            domain?: string
+            expires?: Date
+            maxAge?: number
+            httpOnly?: boolean
+            secure?: boolean
+            sameSite?: "lax" | "strict" | "none"
+        }
+
+        for (const attribute of attributes) {
+            const [rawKey, ...rawValue] = attribute.split("=")
+            const key = rawKey.toLowerCase()
+            const attrValue = rawValue.join("=")
+
+            if (key === "path") options.path = attrValue
+            else if (key === "domain") options.domain = attrValue
+            else if (key === "expires") options.expires = new Date(attrValue)
+            else if (key === "max-age") options.maxAge = Number(attrValue)
+            else if (key === "httponly") options.httpOnly = true
+            else if (key === "secure") options.secure = true
+            else if (key === "samesite") options.sameSite = attrValue.toLowerCase() as "lax" | "strict" | "none"
+        }
+
+        cookieStore.set(name, value, options)
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/next/src/lib/api.ts` around lines 19 - 26, applyCookies currently
strips all Set-Cookie attributes and only writes name/value to cookieStore,
losing HttpOnly/Secure/SameSite/Path/Expires/Max-Age which breaks security and
sign-out; update applyCookies to parse each cookie string (in applyCookies) into
name, value and attribute map and pass the appropriate options into
cookieStore.set (e.g., httpOnly, secure, path, sameSite, expires or maxAge) so
attributes are preserved when syncing auth cookies; you can implement this by
splitting on ";" after the first name=value, normalizing attribute names and
values, converting Expires to a Date and Max-Age to seconds, and mapping
SameSite to the expected enum before calling cookieStore.set(name, value,
options).
apps/tanstack-start/src/components/footer.tsx (1)

63-91: ⚠️ Potential issue | 🟡 Minor

Wrap footer links in list items.

The <ul> elements currently render <a> as direct children. Use <li> wrappers so the footer keeps valid list semantics for assistive technologies.

Proposed fix
 {resources.map(({ href, title }, i) => (
-    <a
-        key={i}
-        className="w-max py-1 text-xs text-muted-foreground duration-200 hover:underline"
-        href={href}
-        target="_blank"
-        rel="noopener noreferrer"
-    >
-        {title}
-    </a>
+    <li key={i}>
+        <a
+            className="w-max py-1 text-xs text-muted-foreground duration-200 hover:underline"
+            href={href}
+            target="_blank"
+            rel="noopener noreferrer"
+        >
+            {title}
+        </a>
+    </li>
 ))}
@@
 {community.map(({ href, title }, i) => (
-    <a
-        key={i}
-        className="w-max py-1 text-xs text-muted-foreground duration-200 hover:underline"
-        href={href}
-        target="_blank"
-        rel="noopener noreferrer"
-    >
-        {title}
-    </a>
+    <li key={i}>
+        <a
+            className="w-max py-1 text-xs text-muted-foreground duration-200 hover:underline"
+            href={href}
+            target="_blank"
+            rel="noopener noreferrer"
+        >
+            {title}
+        </a>
+    </li>
 ))}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/tanstack-start/src/components/footer.tsx` around lines 63 - 91, The
footer currently renders anchors directly inside <ul> for the resources and
community lists (mapping over resources and community); wrap each <a> in an <li>
instead to preserve valid list semantics and assistive tech support: move the
key={i} to the <li> (e.g., <li key={i}>) and keep the existing className, href,
target, rel and inner {title} on the <a> inside the <li>, ensuring the <ul
className="flex flex-col gap-1"> children are <li> elements rather than anchors.
🟠 Major comments (17)
packages/core/package.json-30-30 (1)

30-30: ⚠️ Potential issue | 🟠 Major

Remove sideEffects: true—this package has no top-level side effects.

The current setting disables tree-shaking for all consumers. Analysis shows all window and setTimeout usages are inside function bodies, not at module initialization. Since the core package has no intentional top-level side effects, change this to:

-  "sideEffects": true,
+  "sideEffects": false,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/package.json` at line 30, Update the package.json "sideEffects"
setting for the core package: replace the current "sideEffects": true entry with
"sideEffects": false (or remove the field entirely) so bundlers can safely
tree-shake this package; locate the "sideEffects" key in
packages/core/package.json and change its value accordingly.
apps/nextjs/pages-router/src/components/edit-profile.tsx-55-60 (1)

55-60: ⚠️ Potential issue | 🟠 Major

Cancel button submits the form instead of closing the dialog.

Both buttons are inside a <form> element (line 31) with action={updateSession}. Without an explicit type attribute, <button> defaults to type="submit" inside a form. This means clicking "Cancel" will trigger the form submission before setIsOpen(false) runs. Set type="button" on Cancel and type="submit" on Edit for explicit intent.

Proposed fix
-                        <Button className="w-full mt-6" variant="default">
+                        <Button type="submit" className="w-full mt-6" variant="default">
                             Edit
                         </Button>
-                        <Button className="w-full mt-6" variant="secondary" onClick={() => setIsOpen(false)}>
+                        <Button type="button" className="w-full mt-6" variant="secondary" onClick={() => setIsOpen(false)}>
                             Cancel
                         </Button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/pages-router/src/components/edit-profile.tsx` around lines 55 -
60, The Cancel button is submitting the form because it lacks an explicit type;
update the JSX for the two Button components inside the form (the Edit button
and the Cancel button rendered by Button) to be explicit: set the Edit Button to
type="submit" and the Cancel Button to type="button" (the Cancel handler uses
setIsOpen(false)). Locate the Button components (named Button) inside the form
that uses action={updateSession} and add the type attributes accordingly so
Cancel does not trigger the updateSession submission.
apps/tanstack-start/src/components/edit-profile.tsx-54-61 (1)

54-61: ⚠️ Potential issue | 🟠 Major

Cancel button will submit the form.

Inside a <form>, buttons default to type="submit". The Cancel Button on Line 58 has no explicit type, so clicking it will trigger updateSession (submitting the form) in addition to closing the panel. Set type="button" on Cancel (and ideally make the Edit button explicitly type="submit").

Proposed fix
-                        <Button className="w-full mt-6" variant="default">
+                        <Button type="submit" className="w-full mt-6" variant="default">
                             Edit
                         </Button>
-                        <Button className="w-full mt-6" variant="secondary" onClick={() => setIsOpen(false)}>
+                        <Button type="button" className="w-full mt-6" variant="secondary" onClick={() => setIsOpen(false)}>
                             Cancel
                         </Button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/tanstack-start/src/components/edit-profile.tsx` around lines 54 - 61,
The Cancel Button inside the form is missing an explicit type so it defaults to
type="submit" and unintentionally triggers updateSession; update the JSX for the
two buttons in edit-profile (the Button components around the Edit and Cancel
labels) to set the Cancel button to type="button" and explicitly set the Edit
button to type="submit" so Cancel only calls setIsOpen(false) and Edit performs
the form submission.
packages/next/src/lib/api.ts-65-76 (1)

65-76: ⚠️ Potential issue | 🟠 Major

Tie the generic to the input parameter and honor redirect: false.

Line 66 should declare options: O instead of options: SignInCredentialsAPIOptions. The generic O is constrained but not applied to the parameter, so the actual options type doesn't propagate to the return type NextSignInCredentials<O>, causing type drift.

Additionally, line 74 only checks options?.redirectTo before redirecting. It should also check options.redirect !== false, matching the pattern used in React Router (packages/react-router/src/lib/api.ts:57) and respecting the contract of NextSignInCredentials<O>, which returns never when redirect: true is passed.

Proposed adjustment
 export const signInCredentials = <DefaultUser extends User = User>({ api }: AuthInstance<DefaultUser>) => {
     return async <O extends SignInCredentialsAPIOptions>(
-        options: SignInCredentialsAPIOptions
+        options: O
     ): Promise<NextSignInCredentials<O>> => {
         const signIn = await api.signInCredentials({
             headers: await headers(),
             ...options,
             payload: options.payload,
         })
         await applyCookies(signIn.headers)
-        if (signIn.success && options?.redirectTo) {
+        if (signIn.success && options.redirect !== false && signIn.redirectURL) {
             redirect(signIn.redirectURL)
         }
         return signIn as NextSignInCredentials<O>
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/next/src/lib/api.ts` around lines 65 - 76, Change the parameter type
of the returned async function from SignInCredentialsAPIOptions to the generic O
(i.e. options: O) so the input options propagate to the return type
NextSignInCredentials<O>; keep the constraint O extends
SignInCredentialsAPIOptions. After receiving signIn and applying
applyCookies(signIn.headers), only call redirect(signIn.redirectURL) when
options.redirect !== false AND options?.redirectTo is truthy (mirror the
react-router pattern) so cases with redirect: false are honored. Ensure
references in this function to api.signInCredentials, headers(), applyCookies,
redirectTo, and redirect remain unchanged except for the type and conditional
redirect check.
apps/react-router/app/components/edit-profile.tsx-54-60 (1)

54-60: ⚠️ Potential issue | 🟠 Major

Set explicit form button types.

Inside a form, an untyped native button submits by default. The Button component spreads props without applying a default type, so "Cancel" will call updateSession and update the session instead of only closing the form. Make both buttons explicit.

🐛 Proposed fix
-                        <Button className="w-full mt-6" variant="default">
+                        <Button className="w-full mt-6" variant="default" type="submit">
                             Edit
                         </Button>
-                        <Button className="w-full mt-6" variant="secondary" onClick={() => setIsOpen(false)}>
+                        <Button className="w-full mt-6" variant="secondary" type="button" onClick={() => setIsOpen(false)}>
                             Cancel
                         </Button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/react-router/app/components/edit-profile.tsx` around lines 54 - 60, The
two Button instances in the EditProfile component are missing explicit button
types so the "Cancel" button (which calls setIsOpen(false)) will act as a
submit; update both Button usages to include explicit type props: set the Edit
Button (which triggers the form submit handler like updateSession) to
type="submit" and the Cancel Button to type="button" to prevent implicit
submission.
apps/nextjs/app-router/src/components/edit-profile.tsx-55-60 (1)

55-60: ⚠️ Potential issue | 🟠 Major

Add explicit form button types.

Inside a form, an untyped native button submits by default. The Button component spreads props without a default type, so the Cancel button will call updateSession instead of only closing the form.

Proposed fix
-                        <Button className="w-full mt-6" variant="default">
+                        <Button className="w-full mt-6" variant="default" type="submit">
                             Edit
                         </Button>
-                        <Button className="w-full mt-6" variant="secondary" onClick={() => setIsOpen(false)}>
+                        <Button className="w-full mt-6" variant="secondary" type="button" onClick={() => setIsOpen(false)}>
                             Cancel
                         </Button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/app-router/src/components/edit-profile.tsx` around lines 55 - 60,
The Cancel button in the form is missing an explicit type so it defaults to
submit and triggers updateSession; update the two buttons inside the grid so the
primary "Edit" Button has type="submit" and the Cancel Button (the one with
onClick={() => setIsOpen(false)}) has type="button" (ensure the Button component
accepts/passes through the type prop if needed) to prevent accidental form
submission.
apps/tanstack-start/src/routes/index.tsx-69-77 (1)

69-77: ⚠️ Potential issue | 🟠 Major

Use a native anchor for the external card link.

The flow[0].href is an absolute URL (https://aura-stack-auth.vercel.app/), but the code renders it through TanStack Router's Link component. The Link component's to prop does not support absolute external URLs and will produce TypeScript errors. Use a native <a href> for external navigation instead. Keep the router Link for internal routes like /server and /client.

🐛 Proposed fix
                             <Button className="w-min mt-6" variant="outline" asChild>
-                                <Link
-                                    to={item.href}
-                                    target={item.external ? "_blank" : undefined}
-                                    rel={item.external ? "noopener noreferrer" : undefined}
-                                >
-                                    Try Now
-                                </Link>
+                                {item.external ? (
+                                    <a href={item.href} target="_blank" rel="noopener noreferrer">
+                                        Try Now
+                                    </a>
+                                ) : (
+                                    <Link to={item.href}>Try Now</Link>
+                                )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/tanstack-start/src/routes/index.tsx` around lines 69 - 77, Replace the
TanStack Router Link usage for absolute/external URLs: inside the Button/asChild
block, check item.external (or specifically flow[0].href when applicable) and
when true render a native anchor element with href={item.href} and proper
target={item.external ? "_blank" : undefined} and rel={item.external ? "noopener
noreferrer" : undefined}; otherwise keep using the router Link with
to={item.href}. Update the conditional rendering around Button -> Link/anchor to
avoid passing absolute URLs into the Link component (which causes TypeScript
errors).
apps/nextjs/pages-router/src/pages/client/index.tsx-79-87 (1)

79-87: ⚠️ Potential issue | 🟠 Major

Prevent the sign-out button from submitting the form.

Inside a form, a button defaults to type="submit". This can trigger a native form submission/reload while signOut() is still running.

Proposed fix
-<Button className="w-20" variant="default" onClick={handleSignOut}>
+<Button className="w-20" variant="default" type="button" onClick={handleSignOut}>
     Sign Out
 </Button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/pages-router/src/pages/client/index.tsx` around lines 79 - 87,
The Sign Out Button inside the form defaults to submitting the form; modify the
Button usage in pages/client/index.tsx (the Button that calls handleSignOut) to
explicitly set type="button" so clicking it does not trigger form submission or
page reload while signOut() runs; ensure you update the Button element that
currently calls handleSignOut to include the type attribute and keep the onClick
handler unchanged.
apps/tanstack-start/src/routes/server.tsx-31-47 (1)

31-47: ⚠️ Potential issue | 🟠 Major

Refresh loader-backed auth state after credentials sign-in/sign-out.

This page derives isAuthenticated from loader data, but these server mutations do not invalidate the route. If the server function returns without a full redirect, the UI can keep showing the stale pre-mutation session.

Proposed fix
 const handleSignOut = async () => {
     await signOut()
+    await router.invalidate()
 }
 
 const handleSignInCredentials = async (event: FormEvent<HTMLFormElement>) => {
     event.preventDefault()
     const formData = new FormData(event.currentTarget)
@@
         },
     })
+    await router.invalidate()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/tanstack-start/src/routes/server.tsx` around lines 31 - 47, The
loader-backed auth state isn't refreshed after calling signOut() or
signInCredentials(), so update handleSignOut and handleSignInCredentials to
trigger a loader revalidation once the async auth call completes (e.g., get a
revalidator via useRevalidator() or the equivalent route revalidation hook and
call revalidator.revalidate() after the await). Specifically, after the await
signOut() in handleSignOut and after the await signInCredentials(...) in
handleSignInCredentials, invoke the route revalidation method so the
loader-derived isAuthenticated value is refreshed.
apps/nextjs/pages-router/src/pages/client/index.tsx-57-64 (1)

57-64: ⚠️ Potential issue | 🟠 Major

Configure images.remotePatterns for OAuth avatar hosts or use a plain <img> tag.

The app supports GitHub, Gitlab, and Bitbucket OAuth providers, which return external avatar URLs from github.com, gitlab.com, and bitbucket.com. Without images.remotePatterns or images.domains in next.config.ts, the next/image component will throw at runtime when attempting to load these external avatars.

Either configure the image hosts in next.config.ts:

{
  images: {
    remotePatterns: [
      { protocol: 'https', hostname: 'avatars.githubusercontent.com' },
      { protocol: 'https', hostname: 'gitlab.com' },
      { protocol: 'https', hostname: 'bitbucket.org' },
    ],
  },
}

Or replace the next/image component with a standard <img> tag for user-provided URLs.

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

In `@apps/nextjs/pages-router/src/pages/client/index.tsx` around lines 57 - 64,
The Next.js Image usage in the client page (the Image component rendering
session.user.image in pages/client/index.tsx) will fail for external OAuth
avatars from github.com, gitlab.com, and bitbucket.com unless those hosts are
whitelisted; update the Next.js image config by adding images.remotePatterns (or
images.domains) in next.config.js to include avatars.githubusercontent.com,
gitlab.com, and bitbucket.org, or replace the next/image usage in the component
that renders session.user.image with a plain <img> tag to accept external URLs;
locate the Image usage in this file (the JSX that checks session?.user?.image)
and apply one of these fixes.
apps/nuxt/app/composables/useAuthClient.ts-14-16 (1)

14-16: ⚠️ Potential issue | 🟠 Major

Do not ignore a provided initialSession.

If the shared aura-session state already contains stale data, a server-provided null or updated session is skipped and onMounted also avoids refreshing. That can render an authenticated UI after the server determined there is no session.

Proposed fix
-    if (initialSession !== undefined && session.value === null) {
+    if (initialSession !== undefined) {
         session.value = initialSession
     }
@@
     onMounted(async () => {
-        if (!session.value) {
+        if (initialSession === undefined && !session.value) {
             await refresh()
         }
     })

Also applies to: 73-76

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

In `@apps/nuxt/app/composables/useAuthClient.ts` around lines 14 - 16, The code
currently only assigns initialSession when initialSession !== undefined AND
session.value === null, which ignores server-provided nulls or updated sessions
and leaves stale state; change the logic to assign session.value whenever
initialSession !== undefined (so that null overwrites stale session), i.e.
replace the conditional that checks session.value === null with a simple
initialSession !== undefined check for the assignment; apply the same change to
the other occurrence (the block around lines 73-76) so both spots that reference
initialSession and session.value use the corrected condition and ensure any
onMounted refresh logic relies on this updated session value.
apps/nextjs/pages-router/src/pages/server/index.tsx-10-19 (1)

10-19: ⚠️ Potential issue | 🟠 Major

Return the fetched session as initialSession.

getServerSideProps calls api.getSession(...) but then discards the result, so the SSR page always hydrates as unauthenticated.

Proposed fix
     return {
         props: {
-            initialSession: null,
+            initialSession: session,
         },
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/pages-router/src/pages/server/index.tsx` around lines 10 - 19,
getServerSideProps calls api.getSession(...) but always returns initialSession:
null; update the return to pass the fetched session so the page hydrates
authenticated. Specifically, in getServerSideProps replace the hardcoded
initialSession: null with initialSession: session (the value returned by
api.getSession) so the server-provided session is included in props for
hydration.
apps/nextjs/app-router/src/app/server/page.tsx-17-45 (1)

17-45: ⚠️ Potential issue | 🟠 Major

Redirect back to the /server route, not /ssr.

This page is mounted at /server, but sign-out, credentials sign-in, and session update redirect to /ssr, which makes the flow land on the wrong route.

Proposed fix
         await api.signOut({
-            redirectTo: "/ssr",
+            redirectTo: "/server",
         })
@@
             payload: {
                 username,
                 password,
             },
-            redirectTo: "/ssr",
+            redirectTo: "/server",
         })
@@
-        redirect("/ssr")
+        redirect("/server")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/app-router/src/app/server/page.tsx` around lines 17 - 45, The
redirect targets in signOutAction, signInCredentialsAction, and
updateSessionAction are pointing to "/ssr" but this page is mounted at
"/server"; update each call to use the correct route by changing redirectTo:
"/ssr" to redirectTo: "/server" in api.signOut and api.signInCredentials, and
change redirect("/ssr") to redirect("/server") in updateSessionAction so all
flows return to the /server route.
apps/react-router/app/routes/client/index.tsx-77-87 (1)

77-87: ⚠️ Potential issue | 🟠 Major

Add type="button" to prevent the button from submitting the surrounding form.

The button on line 84 is nested inside a <form> element. Without an explicit type attribute, it defaults to type="submit", which will trigger a form submission in addition to executing onClick={handleSignOut}. The Button component properly forwards the native HTML type prop to the underlying <button> element, so the fix is straightforward:

Proposed fix
-                            <Button className="w-20" variant="default" onClick={handleSignOut}>
+                            <Button className="w-20" variant="default" type="button" onClick={handleSignOut}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/react-router/app/routes/client/index.tsx` around lines 77 - 87, The Sign
Out Button inside the surrounding <form> is missing an explicit type and thus
defaults to type="submit", causing an unwanted form submission when clicked;
update the Button instance (the one with className="w-20" variant="default"
onClick={handleSignOut}) to include type="button" so it only invokes
handleSignOut and does not submit the form (the Button component already
forwards native button props).
apps/nextjs/pages-router/src/pages/server/index.tsx-93-103 (1)

93-103: ⚠️ Potential issue | 🟠 Major

Set the sign-out button to type="button".

The button defaults to type="submit" within the form, which can trigger form submission and cause a page reload while signOut() is executing. Since the Button component forwards props to the underlying button element, adding type="button" will prevent unwanted form submission behavior.

Proposed fix
-                            <Button className="w-20" variant="default" onClick={handleSignOut}>
+                            <Button className="w-20" variant="default" type="button" onClick={handleSignOut}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/pages-router/src/pages/server/index.tsx` around lines 93 - 103,
The Sign Out Button inside the form is missing an explicit type, causing it to
act as a submit button and potentially trigger form submission when
handleSignOut runs; update the Button component usage (the Button element that
calls handleSignOut in pages/server/index.tsx) to include type="button" so it
does not submit the surrounding <form> and will only invoke the handleSignOut
handler.
apps/tanstack-start/src/routes/client.tsx-83-93 (1)

83-93: ⚠️ Potential issue | 🟠 Major

Add type="button" to prevent form submission.

Without an explicit type attribute, the button defaults to type="submit" and will submit the parent form. Since the form has no onSubmit handler, the submission will trigger a full page reload, interrupting the handleSignOut() execution.

Proposed fix
-                            <Button className="w-20" variant="default" onClick={handleSignOut}>
+                            <Button className="w-20" variant="default" type="button" onClick={handleSignOut}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/tanstack-start/src/routes/client.tsx` around lines 83 - 93, The Sign Out
Button inside the form is being treated as a submit button by the browser,
causing a full page reload and interrupting handleSignOut; update the Button
component (the one rendering Sign Out with onClick={handleSignOut}) to include
type="button" so it does not submit the parent form (alternatively convert the
surrounding <form> to a <div>, but preferred fix is adding the type prop to the
Button).
apps/nextjs/app-router/src/app/client/page.tsx-80-90 (1)

80-90: ⚠️ Potential issue | 🟠 Major

Add type="button" to prevent accidental form submission.

The sign-out button sits inside a form without specifying type="button", causing it to default to type="submit". This triggers form submission and can reload the page while handleSignOut() is running.

Proposed fix
-                            <Button className="w-20" variant="default" onClick={handleSignOut}>
+                            <Button className="w-20" variant="default" type="button" onClick={handleSignOut}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/app-router/src/app/client/page.tsx` around lines 80 - 90, The
Sign Out button inside the form is missing an explicit type and defaults to
submitting the form; update the Button component usage in the form that contains
handleSignOut to include type="button" so clicking it invokes handleSignOut
without triggering form submission or a page reload; locate the JSX form and the
Button element (the one with onClick={handleSignOut}) and add the type prop to
that Button.
🟡 Minor comments (10)
apps/nextjs/app-router/src/components/footer.tsx-36-36 (1)

36-36: ⚠️ Potential issue | 🟡 Minor

Align the footer bottom row with the widened wrapper.

Line 36 widens the footer shell to max-w-6xl, but Line 84 still caps the copyright row at max-w-5xl, unlike the matching footer updates in the other examples. This can leave the bottom row visually misaligned with the new border/header width.

🎨 Proposed alignment fix
-                <div className="max-w-5xl py-4 flex flex-col justify-between gap-2">
+                <div className="max-w-6xl py-4 flex flex-col justify-between gap-2">

Also applies to: 84-84

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

In `@apps/nextjs/app-router/src/components/footer.tsx` at line 36, The footer's
outer wrapper was widened to max-w-6xl but the bottom copyright row still uses
max-w-5xl, causing misalignment; in the Footer component locate the element
whose className contains "max-w-5xl" (the copyright/ bottom row) and change it
to "max-w-6xl" so the bottom row matches the widened wrapper's width and border
alignment.
apps/nextjs/pages-router/src/lib/auth.ts-20-20 (1)

20-20: ⚠️ Potential issue | 🟡 Minor

Same locale-aware lowercasing issue as in the app-router auth.ts.

Switch toLocaleLowerCase() to toLowerCase() to avoid locale-dependent casing (e.g., Turkish Iı) when building the email.

Proposed fix
-                email: `${username.toLocaleLowerCase()}@example.com`,
+                email: `${username.toLowerCase()}@example.com`,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/pages-router/src/lib/auth.ts` at line 20, Replace the
locale-aware casing call used when constructing the email: change the use of
username.toLocaleLowerCase() in the auth email construction (the line setting
email: `${username.toLocaleLowerCase()}@example.com`) to username.toLowerCase()
so the generated email uses locale-independent lowercasing; update the
occurrence inside the auth builder/function in auth.ts accordingly.
apps/nextjs/app-router/src/lib/auth.ts-20-20 (1)

20-20: ⚠️ Potential issue | 🟡 Minor

Use toLowerCase() instead of toLocaleLowerCase() for email normalization.

Email local-parts are ASCII and locale-sensitive lowercasing can yield unexpected results (e.g., Turkish locale maps Iı). For normalizing identifiers like emails, prefer toLowerCase().

Proposed fix
-                email: `${username.toLocaleLowerCase()}@example.com`,
+                email: `${username.toLowerCase()}@example.com`,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/app-router/src/lib/auth.ts` at line 20, Replace the locale-aware
lowercasing call on the username used for generating the email — change the
template string that currently uses username.toLocaleLowerCase() to use
username.toLowerCase() instead so email normalization uses locale-independent
lowercasing; locate the occurrence in the email template (the template string
with `${username.toLocaleLowerCase()}@example.com`) in auth.ts and update it to
`${username.toLowerCase()}@example.com`.
apps/tanstack-start/src/components/edit-profile.tsx-16-18 (1)

16-18: ⚠️ Potential issue | 🟡 Minor

htmlFor="signout" points to a non-existent element.

The label targets id="signout" but there is no such input in this component (this is the Edit Profile section). Either remove htmlFor or point it to the intended control.

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

In `@apps/tanstack-start/src/components/edit-profile.tsx` around lines 16 - 18,
The label in the Edit Profile component uses htmlFor="signout" which doesn't
match any element id; update the label to either remove the htmlFor attribute or
change it to the correct target id for the intended control (e.g., the
input/button related to editing profile), locating the label element rendered in
the Edit Profile component (the <label className="font-medium block"
htmlFor="signout"> element) and ensure the referenced id exists on the
corresponding input/button or remove htmlFor if no explicit control should be
associated.
apps/tanstack-start/src/lib/auth-server.ts-89-98 (1)

89-98: ⚠️ Potential issue | 🟡 Minor

Rename the input field from username to name for consistency with the session schema.

The code correctly maps to user.name as required by the session schema (which defines only a name field, not username). However, the input validator accepting username is inconsistent with the output. Rename the parameter to name to align with the session's actual user structure:

Current state
.inputValidator((data: { username?: string; email?: string }) => data)
.handler(async ({ data }) => {
    const response = await api
        .updateSession({
            session: {
                user: {
                    name: data.username,
                    email: data.email,
                },

The session user object expects name (not username), so the input parameter should reflect this naming for clarity.

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

In `@apps/tanstack-start/src/lib/auth-server.ts` around lines 89 - 98, Update the
input shape used by the route so it uses name instead of username: change the
inputValidator signature/type from { username?: string; email?: string } to {
name?: string; email?: string } and in the handler update the mapping passed to
api.updateSession so it assigns user.name = data.name (and user.email =
data.email remains unchanged); update any references inside the same
handler/function (e.g., the .inputValidator and .handler that call
updateSession) to use data.name for consistency with the session schema.
apps/tanstack-start/src/styles.css-5-17 (1)

5-17: ⚠️ Potential issue | 🟡 Minor

Fix the stylelint failures in the font stack.

Stylelint is flagging the changed body block for the declaration spacing and unnecessary quotes around single-word font family names.

Proposed stylelint-compatible adjustment
 body {
   `@apply` m-0;
+
   font-family:
-    -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
-    "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
+    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
+    Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/tanstack-start/src/styles.css` around lines 5 - 17, The body and code
font-family declarations violate stylelint rules: remove unnecessary quotes from
single-word font family names and normalize spacing/commas; in the body selector
(font-family) unquote single-word names like "Roboto" and "sans-serif" and
ensure each family is comma + space separated and the property uses a single
contiguous value (you can keep multi-word names quoted, e.g., "Segoe UI",
"Helvetica Neue"); do the same for the code selector (unquote source-code-pro
and other single-word names) so both font-family properties conform to stylelint
spacing and quoting rules.
apps/nuxt/server/api/auth/session.get.ts-4-6 (1)

4-6: ⚠️ Potential issue | 🟡 Minor

The type cast masks potential undefined header values from h3's getHeaders().

h3's getHeaders(event) returns Record<string, string | string[] | undefined>, but the cast to Record<string, string> suppresses the possibility of undefined values and multi-value headers. While the Headers constructor likely tolerates this in practice, the cast hides a real type mismatch and makes intent unclear.

🔧 Suggested approach

Either pass headers directly (as other frameworks do) if the core API tolerates the h3 type, or filter undefined values:

-    const response = await api.getSession({
-        headers: getHeaders(event) as Record<string, string>,
-    })
+    const headers = Object.fromEntries(
+        Object.entries(getHeaders(event)).filter(([, v]) => v !== undefined)
+    ) as Record<string, string>
+    const response = await api.getSession({ headers })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nuxt/server/api/auth/session.get.ts` around lines 4 - 6, The
getHeaders(event) call is being unsafely cast to Record<string,string> before
passing to api.getSession; remove the cast and either pass the raw headers
through if api.getSession accepts Record<string,string|string[]|undefined>, or
normalize them first by converting string[] to a single string (e.g., join with
",") and stripping undefined values into a Record<string,string> before calling
api.getSession; update the call site using getHeaders and api.getSession to
perform this normalization (or pass raw headers) so the type mismatch is
resolved and multi-value/undefined headers are handled explicitly.
apps/nextjs/pages-router/src/pages/client/index.tsx-3-14 (1)

3-14: ⚠️ Potential issue | 🟡 Minor

Add type keyword to the import statement.

SubmitEvent is properly exported from React 19.2.10+ (@types/react 19.2.14) and correctly used throughout the codebase. However, type-only imports should use the type keyword for clarity and to avoid unnecessary runtime bundling.

Proposed fix
-import { SubmitEvent } from "react"
+import type { SubmitEvent } from "react"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/pages-router/src/pages/client/index.tsx` around lines 3 - 14, The
import of SubmitEvent should be a type-only import to avoid runtime bundling;
update the import statement that currently reads "import { SubmitEvent } from
'react'" to use the type keyword (import type { SubmitEvent } from 'react') so
that the type-only symbol used in handleSignInCredentials (and elsewhere in this
file) is erased at compile time and does not produce runtime import overhead.
apps/nuxt/app/composables/useAuthClient.ts-18-24 (1)

18-24: ⚠️ Potential issue | 🟡 Minor

Make refresh() drive the loading state used by status.

Initial client refreshes currently leave isLoading as false, so status reports "unauthenticated" until the request finishes instead of "loading".

Proposed fix
     const refresh = async () => {
+        isLoading.value = true
         try {
             session.value = await authClient.getSession()
         } catch {
             session.value = null
+        } finally {
+            isLoading.value = false
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nuxt/app/composables/useAuthClient.ts` around lines 18 - 24, The
refresh() function should drive the isLoading state so status reflects "loading"
during the fetch: set isLoading (the reactive flag used by status) to true
immediately before calling await authClient.getSession(), then set isLoading
back to false in a finally block after the await; update session.value with the
result on success and set it to null on error (catch) as before, ensuring
isLoading is always cleared even if authClient.getSession() throws. Target
symbols: refresh(), isLoading, status, session.value, authClient.getSession().
apps/react-router/app/routes/server.tsx-28-41 (1)

28-41: ⚠️ Potential issue | 🟡 Minor

Validate provider and handle credentials failures.

  • provider is read from untrusted form input and passed straight to api.signIn without validating it against the allowed list (github/gitlab/bitbucket). Constrain it to a known set to avoid unexpected provider lookups or misleading errors.
  • api.signInCredentials has no try/catch and no way to surface failure to the UI — invalid credentials will either throw (500 to the client) or silently no-op. Consider returning an error payload that the page can render.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/react-router/app/routes/server.tsx` around lines 28 - 41, Validate the
untrusted provider value before calling api.signIn: when actionType === "signIn"
check formData.get("provider") (provider) against an explicit allowlist (e.g.,
"github","gitlab","bitbucket") and return a controlled error/response for
invalid values instead of forwarding them to api.signIn; and for the
"signInCredentials" branch wrap the api.signInCredentials call in a try/catch
(using the username and password extracted from formData) and return a
structured error payload (e.g., { success: false, error: "Invalid credentials" }
or a 400/401 response) so the UI can render credential failures rather than
letting exceptions bubble as 500s or silently no-op.
🧹 Nitpick comments (9)
apps/tanstack-start/src/components/auth-client.tsx (1)

1-12: Remove the unused AuthClient component.

The component is not imported anywhere in the tanstack-start app—only AuthClientPage (a separate route component) and authClient (the auth library client instance) are used. Keeping this stub adds dead code with no benefit.

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

In `@apps/tanstack-start/src/components/auth-client.tsx` around lines 1 - 12,
Remove the dead exported React component AuthClient from
apps/tanstack-start/src/components/auth-client.tsx: delete the entire AuthClient
function and its default/ named export so the unused component and its useAuth
import are eliminated; if other files expect this module, instead export nothing
or refactor imports to use AuthClientPage or authClient, and run a project-wide
search for "AuthClient" to ensure all references are removed.
apps/nuxt/app/components/header.vue (1)

67-73: NuxtLink used for external URL.

NuxtLink is intended for internal routing. For https://aura-stack-auth.vercel.app/docs, use a plain <a> with target="_blank" and rel="noopener noreferrer" to match the other external links in this header and avoid relying on NuxtLink's external-URL handling.

Proposed fix
-                <NuxtLink
-                    to="https://aura-stack-auth.vercel.app/docs"
+                <a
+                    href="https://aura-stack-auth.vercel.app/docs"
                     class="text-sm text-muted-foreground hover:text-white transition-colors py-2"
+                    target="_blank"
+                    rel="noopener noreferrer"
                     `@click`="closeMenu"
                 >
                     Documentation
-                </NuxtLink>
+                </a>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nuxt/app/components/header.vue` around lines 67 - 73, Replace the
external URL usage of NuxtLink with a normal anchor: change the <NuxtLink ...>
element that points to "https://aura-stack-auth.vercel.app/docs" to an <a>
element, keep the same classes and `@click`="closeMenu" handler, and add
target="_blank" rel="noopener noreferrer" so the link behaves like the other
external links in this header component and avoids using NuxtLink for external
navigation.
apps/tanstack-start/src/components/header.tsx (1)

113-117: Consider disabling the sign-in button while pending/submitting.

Currently the button is hidden while isPending, which can cause layout shift on load. Consider keeping it rendered but disabled during isPending for a smoother UX, and also preventing double-submits while signIn("github") is in flight.

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

In `@apps/tanstack-start/src/components/header.tsx` around lines 113 - 117, Render
the Sign in with GitHub Button even when isPending instead of hiding it to avoid
layout shift: change the conditional that uses isPending and isAuthenticated so
the Button is rendered whenever !isAuthenticated and pass disabled={isPending}
(and optionally show a spinner) to prevent clicks while pending; also guard
handleSignIn (or the signIn("github") call) so it no-ops if isPending to prevent
double-submits (use the existing isPending flag or a local submitting flag
inside handleSignIn).
apps/tanstack-start/src/lib/auth-server.ts (1)

86-88: inputValidator does not validate.

The validator is a pass-through and will accept any payload shape, defeating the purpose of inputValidator and allowing non-string fields to flow into api.updateSession. Add shape/type checks similar to signInCredentialsFn.

Proposed fix
-    .inputValidator((data: { username?: string; email?: string }) => data)
+    .inputValidator((data: { username?: string; email?: string }) => {
+        if (!data || typeof data !== "object") {
+            throw new Error("update session payload is invalid")
+        }
+        if (data.username !== undefined && typeof data.username !== "string") {
+            throw new Error("username must be a string")
+        }
+        if (data.email !== undefined && typeof data.email !== "string") {
+            throw new Error("email must be a string")
+        }
+        return data
+    })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/tanstack-start/src/lib/auth-server.ts` around lines 86 - 88, The
inputValidator for updateSessionFn currently just returns the input and accepts
any shape; change it to validate that data is an object with optional username
and email that, if present, are strings (mirroring the signInCredentialsFn
validation) and throw or return an error for invalid types so only string values
reach api.updateSession; update the inputValidator used by updateSessionFn to
enforce these shape/type checks and reject non-string fields.
apps/nextjs/app-router/next.config.ts (1)

5-12: LGTM!

remotePatterns for avatars.githubusercontent.com is the correct approach for Next.js 15 image optimization. Consider also setting pathname: "/u/**" to narrow the allowed paths if only GitHub user avatars are rendered.

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

In `@apps/nextjs/app-router/next.config.ts` around lines 5 - 12, The Next.js
images.remotePatterns currently allows avatars.githubusercontent.com broadly; to
restrict to GitHub user avatars, update the remotePatterns entry (the
images.remotePatterns array in your next config) to include a pathname filter
like "/u/**" so only paths under /u/ are permitted; locate the images config in
next.config.ts and add the pathname field to the existing object alongside
protocol and hostname.
apps/nextjs/app-router/src/app/layout.tsx (1)

26-26: Remove the commented-out <Footer /> placeholder.

Dead/commented code tends to rot. If the Footer will be reintroduced, track it via an issue/TODO; otherwise drop the comment.

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

In `@apps/nextjs/app-router/src/app/layout.tsx` at line 26, Remove the
commented-out "<Footer />" placeholder from the JSX in layout.tsx (the commented
line containing {/* <Footer /> */}); either delete the comment outright or
replace it with a brief TODO reference to an issue number if the Footer will be
reintroduced, ensuring no dead/commented JSX remains.
packages/core/test/api/signOut.test.ts (1)

39-44: Assert that sign-out actually clears the session cookie.

These tests currently accept any Headers object, so a regression that returns success: true without expiring aura-auth.session_token would still pass.

Suggested assertion to add to each success case
 expect(out).toEqual({
     success: true,
     redirectURL: "/",
     headers: expect.any(Headers),
     toResponse: expect.any(Function),
 })
+
+const setCookie = out.headers.get("set-cookie")
+expect(setCookie).toContain("aura-auth.session_token=")
+expect(setCookie).toMatch(/(?:Max-Age=0|Expires=)/i)

Also applies to: 61-66, 84-89

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

In `@packages/core/test/api/signOut.test.ts` around lines 39 - 44, The tests
currently only assert success and accept any Headers, so update each
success-case assertion (the expects that currently check headers:
expect.any(Headers) in signOut.test.ts) to explicitly verify the Set-Cookie
header clears the session cookie: read the Set-Cookie from out.headers (e.g.,
out.headers.get('set-cookie')) and assert it contains the cookie name
"aura-auth.session_token" and an expiry/clear directive such as "Max-Age=0" or
an expired date (e.g., "Expires=Thu, 01 Jan 1970") or the cookie value being
empty; apply the same explicit Set-Cookie assertion to the other two success
blocks (the assertions around lines 61-66 and 84-89) so a regression that
returns success without expiring the session_token will fail.
apps/react-router/app/routes/server.tsx (2)

162-189: Add autoComplete and required on credential inputs.

The username/password inputs lack autoComplete hints (username, current-password) and required, which hurts password-manager integration and lets empty submissions hit the server. Minor UX/a11y polish.

♻️ Proposed fix
-                                    <input
-                                        type="text"
-                                        id="username"
-                                        name="username"
+                                    <input
+                                        type="text"
+                                        id="username"
+                                        name="username"
+                                        autoComplete="username"
+                                        required
                                         className="..."
                                     />
@@
-                                    <input
-                                        type="password"
-                                        id="password"
-                                        name="password"
+                                    <input
+                                        type="password"
+                                        id="password"
+                                        name="password"
+                                        autoComplete="current-password"
+                                        required
                                         className="..."
                                     />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/react-router/app/routes/server.tsx` around lines 162 - 189, The username
and password inputs in the Form used by handleSignInCredentials are missing
autoComplete hints and required attributes, allowing empty submissions and
hindering password managers; update the <input id="username" name="username"> to
include required and autoComplete="username" and update the <input id="password"
name="password"> to include required and autoComplete="current-password" so the
Form handler (handleSignInCredentials) receives validated values and
browsers/password managers can autofill correctly (keep the existing ids/names
and Button unchanged).

8-14: Drop the as any cast on the loader return.

Casting to any discards the inferred session type and propagates any into loaderData in the component, defeating React Router's typed route data. Return the object directly and let the type flow.

♻️ Proposed fix
-    return { session } as any
+    return { session }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/react-router/app/routes/server.tsx` around lines 8 - 14, Remove the
unnecessary "as any" cast in the loader so the inferred session type is
preserved; in the export const loader async function that takes ({ request }:
Route.LoaderArgs) and calls api.getSession, return { session } directly (or
explicitly type the loader return if you prefer) instead of "return { session }
as any" so the session type flows into loaderData in consuming components.

Comment thread apps/nextjs/app-router/src/app/client/page.tsx Outdated
Comment thread apps/nuxt/app/pages/client.vue Outdated
Comment thread apps/nuxt/app/pages/index.vue
Comment thread apps/nuxt/app/pages/server.vue
…146)

* feat(packages): enhance API type definitions and response structures

* chore: update README.md files
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: 19

Caution

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

⚠️ Outside diff range comments (4)
packages/core/src/api/getSession.ts (1)

16-24: ⚠️ Potential issue | 🟡 Minor

Include the new error metadata in toResponse().

The returned object now has error, but the generated 401 response drops it, so programmatic and HTTP consumers see different failure shapes.

Proposed fix
+    const unauthorizedError = {
+        code: "GET_SESSION_FAILED",
+        message: "Failed to retrieve session. The session token may be missing, expired, or invalid.",
+    } as const
     const unauthorized: GetSessionAPIReturn<DefaultUser> = {
         session: null,
         headers,
         success: false,
-        error: {
-            code: "GET_SESSION_FAILED",
-            message: "Failed to retrieve session. The session token may be missing, expired, or invalid.",
-        },
-        toResponse: () => Response.json({ success: false, session: null }, { status: 401, headers }),
+        error: unauthorizedError,
+        toResponse: () =>
+            Response.json({ success: false, session: null, error: unauthorizedError }, { status: 401, headers }),
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/api/getSession.ts` around lines 16 - 24, The toResponse()
for the unauthorized GetSessionAPIReturn in getSession.ts currently returns {
success:false, session:null } only; update the unauthorized object's toResponse
implementation (the toResponse method on the unauthorized constant) to include
the error metadata (error.code and error.message and/or the entire error object)
in the JSON body so the HTTP 401 response shape matches the returned object
(e.g., return Response.json({ success: false, session: null, error }, { status:
401, headers })).
packages/core/test/actions/signOut/signOut.test.ts (1)

145-184: ⚠️ Potential issue | 🟠 Major

Do not assert redirect: false for 302 responses.

Lines 156 and 178 expect a redirect status, and Line 164 also checks the Location header, but the JSON body is asserted as redirect: false. That locks in a contradictory contract for clients.

Either make these responses non-redirecting, or update the response contract/tests so 302 responses report redirect: true.

Proposed test expectation if 302 is intended
         expect(await request.json()).toMatchObject({
             success: true,
-            redirect: false,
+            redirect: true,
             redirectURL: "/custom-logout-page",
         })
@@
         expect(await request.json()).toMatchObject({
-            redirect: false,
+            redirect: true,
             success: true,
             redirectURL: "/",
         })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/test/actions/signOut/signOut.test.ts` around lines 145 - 184,
The tests "valid sessionToken cookie with redirectTo parameter" and "valid
sessionToken cookie with invalid redirectTo parameter" currently assert a 302
status but also assert the JSON body has redirect: false, which is
contradictory; update these tests to reflect that a 302 response should report
redirect: true (modify the assertions in those two tests to expect redirect:
true and keep the redirectURL checks) and ensure the first test still asserts
the Location header equals "/custom-logout-page"; alternatively, if the intended
behavior is no HTTP redirect, change the response status to 200 in the code
under test and keep redirect: false—adjust whichever side (test assertions or
response status in the signOut handler) to make the status, Location header, and
JSON redirect flag consistent.
packages/core/src/api/credentials.ts (1)

49-78: ⚠️ Potential issue | 🟠 Major

Extract code/message from AuthValidationError in addition to AuthInternalError.

isAuthInternalError(error) only recognizes AuthInternalError instances, but the credentials sign-in endpoint throws AuthValidationError("INVALID_CREDENTIALS", ...) on line 31. When caught, the check on line 52 fails, leaving code and message at their generic defaults. The response then returns code: "CREDENTIALS_SIGN_IN_ERROR" instead of the specific "INVALID_CREDENTIALS", even though the error instanceof AuthValidationError branch on line 66 knows exactly what error occurred. Both error classes have identical structure with code and message properties, so they can be handled together:

🛠️ Proposed fix
         let code = "CREDENTIALS_SIGN_IN_ERROR"
         let message = "An error occurred during credentials sign-in."
-        if (isAuthInternalError(error)) {
+        if (isAuthInternalError(error) || error instanceof AuthValidationError) {
             code = error.code
             message = error.message
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/api/credentials.ts` around lines 49 - 78, The catch block
in the credentials sign-in flow sets generic code/message because
isAuthInternalError(error) doesn't recognize AuthValidationError; update the
catch to extract error.code and error.message for AuthValidationError as well
(or use a type-guard that checks for both structures) before building the
SignInCredentialsAPIReturn; specifically adjust the logic around
isAuthInternalError(error) in the catch so it also handles error instanceof
AuthValidationError (or a shared hasCodeAndMessage guard) and assigns code =
error.code and message = error.message prior to creating invalidCredentials,
keeping the existing logger branches (logger?.log(...)) intact.
packages/core/src/client/client.ts (1)

111-139: ⚠️ Potential issue | 🟠 Major

options.session is dereferenced without a guard.

Line 119-120 does const { session } = options; const user = session.user ?? {}. UpdateSessionOptions<DefaultUser> requires session at the type level, but a consumer passing an untyped object (common in server-action contexts where options is built from FormData, e.g. apps/react-router/app/routes/server/index.tsx at lines 47-52 where { user: {...} } is passed at the top level instead of { session: { user: {...} } }) will hit a runtime Cannot destructure property 'session' of ... / Cannot read properties of undefined (reading 'user'). Consider validating and returning the typed error shape early:

Proposed fix
-            const { session } = options
-            const user = session.user ?? {}
+            const { session } = options ?? {}
+            if (!session) {
+                return { success: false, session: null } as unknown as UpdateSessionReturn<Options, DefaultUser>
+            }
+            const user = session.user ?? {}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/client/client.ts` around lines 111 - 139, The updateSession
function dereferences options.session without guarding against missing/invalid
input; add an early validation at the top of updateSession (the function named
updateSession and the type UpdateSessionOptions) that checks whether options and
options.session exist and are proper objects, and if not return the expected
UpdateSessionReturn shaped failure (e.g. { success: false, session: null } or
include an error field) instead of continuing to destructure; update the
destructuring to use a safe access pattern (avoid const { session } = options
when options may be undefined) and log or throw an AuthClientError consistent
with other error paths so callers get the typed error shape immediately.
🧹 Nitpick comments (5)
packages/react-router/tsdown.config.ts (1)

16-19: Spread shared deps defaults defensively, even though there's no current impact.

The top-level spread of tsdownConfig does not deep-merge nested properties. When you redefine deps on line 16-19, it completely replaces tsdownConfig.deps rather than extending it.

However, the shared config (@aura-stack/tsdown-config) currently only defines onlyBundle: false in deps, and your local override also includes onlyBundle: false, so nothing is actually being dropped today. The risk is future-facing: if shared defaults grow to include additional dependency-resolution fields, they'd be silently lost.

Defensively, merge the shared defaults:

    deps: {
+       ...tsdownConfig.deps,
        onlyBundle: false,
        neverBundle: ["react", "react-dom", "react-router"],
    },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-router/tsdown.config.ts` around lines 16 - 19, The current
override of deps replaces the shared tsdownConfig.deps entirely; update the
override to defensively merge the nested deps (e.g., set deps: {
...tsdownConfig.deps, onlyBundle: false, neverBundle:
["react","react-dom","react-router"] }) so any future fields added to the shared
tsdownConfig.deps are preserved; reference the top-level tsdownConfig and the
deps object when making this change.
packages/next/README.md (1)

79-93: Add type annotations to the client setup example.

The example component lacks TypeScript types for children, which diminishes the "Type-safe by design" claim. Consider adding proper typing to demonstrate best practices.

📝 Suggested improvement with types
-export const App = ({ children }) => {
+export const App = ({ children }: { children: React.ReactNode }) => {
   return <AuthProvider client={client}>{children}</AuthProvider>
 }

Additionally, consider replacing the vague /* your config */ comment with either a minimal example or a reference to the configuration documentation.

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

In `@packages/next/README.md` around lines 79 - 93, The example for
createAuthClient/AuthProvider lacks TypeScript typings for the component props;
update the App component to use explicit types for children (e.g.,
React.ReactNode) and annotate the client variable with the proper auth client
type returned by createAuthClient so the snippet demonstrates type-safe usage;
also replace the placeholder "/* your config */" with either a minimal config
example or a short comment pointing to the configuration docs to clarify what
belongs there.
apps/react-router/app/lib/auth.ts (1)

14-25: Use toLowerCase() (not toLocaleLowerCase()) when constructing an email address.

toLocaleLowerCase() varies with the runtime locale (notably Turkish Iı), which can produce surprising results when the demo is run in a non-en locale. Emails should be normalized with ASCII-safe toLowerCase().

🛠️ Proposed fix
-                email: `${username.toLocaleLowerCase()}@example.com`,
+                email: `${username.toLowerCase()}@example.com`,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/react-router/app/lib/auth.ts` around lines 14 - 25, The authorize
function in the credentials object uses username.toLocaleLowerCase() when
building the email which can yield locale-specific results; change that call to
username.toLowerCase() in the authorize implementation (the block that returns {
sub, name: username, email: `${username.toLocaleLowerCase()}@example.com` }) so
the email is normalized in an ASCII-safe way; no other behavior changes needed
(leave createSecretValue and returned fields intact).
packages/react-router/src/lib/api.ts (1)

42-55: Redundant payload re-assignment after spread.

{ ...options, payload: options.payload } is equivalent to { ...options } — the explicit re-assignment is a no-op. Drop it for clarity:

Proposed refactor
-        const signIn = await api.signInCredentials({
-            ...options,
-            payload: options.payload,
-        })
+        const signIn = await api.signInCredentials({ ...options })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-router/src/lib/api.ts` around lines 42 - 55, In
signInCredentials (the exported function) remove the redundant payload
re-assignment inside the call to api.signInCredentials — replace the object
spread { ...options, payload: options.payload } with simply { ...options } so
you pass the same options without the no-op override; update the call site at
api.signInCredentials(...) accordingly in the signInCredentials function.
packages/next/src/lib/api.ts (1)

105-117: signOut parameter widened to Partial<Options> for no clear reason.

Changing from Options to Partial<Options> makes every field (including required ones like headers in the core SignOutAPIOptions) optional at the call site. That's fine in Next where headers is auto-injected, but it also turns redirect/redirectTo into unknown-ish properties and degrades inference for the conditional return type. Prefer keeping the public parameter as Partial<Pick<Options, "headers">> & Omit<Options, "headers"> — or just Omit<Options, "headers"> & { headers?: HeadersInit } — so the generic stays useful and only headers is optional.

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

In `@packages/next/src/lib/api.ts` around lines 105 - 117, The public parameter
type for signOut was overly widened to Partial<Options>, which makes required
fields (like headers in SignOutAPIOptions) optional and breaks inference for
redirect/redirectTo and the conditional NextSignOutReturn; update the signOut
parameter type in the signOut function so only headers are optional—e.g. use
Omit<Options, "headers"> & { headers?: HeadersInit } or Partial<Pick<Options,
"headers">> & Omit<Options, "headers">—so the generic Options remains useful,
keep the body using api.signOut({ headers: await headers(), ...options }) and
preserve applyCookies/redirect behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/react-router/app/routes/api.auth`.$.tsx:
- Around line 8-14: The action function currently does a manual method dispatch
(inspecting request.method and calling handlers.PATCH or handlers.POST), which
lets unsupported HTTP methods fall through to POST; replace that manual dispatch
with the framework-aware handler by invoking handlers.ALL(request) from the
action function so the framework’s method validation runs and unsupported
methods return 405. Specifically, update the action implementation to call
handlers.ALL(request) instead of the current if/else using handlers.PATCH and
handlers.POST to ensure proper method validation.

In `@apps/react-router/app/routes/server/index.tsx`:
- Around line 33-44: The sign-in branch currently awaits
api.signInCredentials(...) and discards its redirect Response, causing the
action to fall through to return null; update the signInCredentials branch in
the route action handler to return the Response produced by
api.signInCredentials (or its .toResponse()) instead of awaiting and dropping it
so the redirect is sent to the browser (locate the actionType ===
"signInCredentials" block and return the call's Response).
- Around line 46-54: The call to api.updateSession is using the wrong payload
shape and omits the request: wrap the user data inside a session object to match
UpdateSessionOptions (i.e., pass { session: { user: { name, email } } }) and
forward the incoming request so the wrapper can access options.request.headers
(include the request variable from the route handler when invoking
api.updateSession). Update the invocation at the actionType === "updateSession"
block (the api.updateSession call) to build { session: { user: { ... } } } and
include request in the options passed to api.updateSession.

In `@packages/core/CHANGELOG.md`:
- Line 13: In packages/core/CHANGELOG.md remove the standalone dash that creates
an empty list item under the "### Added" section—delete the lone "-" line so the
changelog no longer renders a blank bullet (look for the "### Added" header and
the single "-" list entry and remove that entry).

In `@packages/core/src/`@types/api.ts:
- Around line 59-68: The interface APIOptionsWithRedirectTo currently documents
redirect as defaulting to true but signOut (and other APIs) may default it to
false; update the APIOptionsWithRedirectTo JSDoc to be action-neutral (remove
the "@default `true`" and any wording that claims a universal default) so
defaults are specified per-API (e.g., in signOut's returned redirect logic like
redirect ?? false) and avoid contradictory shared documentation; reference
APIOptionsWithRedirectTo and signOut (the function that sets redirect ?? false)
when making the change.
- Around line 18-23: AuthActionAPIReturn's failure branch currently declares an
error property but its toResponse return type (toResponse: () =>
AuthResponse<Exclude<Body, { success: true }>>) doesn't include that error,
causing a type mismatch with handlers that serialize error (e.g., the
credentials and signIn handlers). Fix by making the failure return type include
the error property (update the failure branch of AuthActionAPIReturn / the
toResponse return type to include error: { code: ErrorCodes; message: string })
so types match implementations, or alternatively remove error serialization from
the handlers (credentials handler and signIn handler) to match the existing
type—choose one consistent approach and update AuthActionAPIReturn and the
toResponse signature (and/or the handlers) accordingly.
- Around line 145-147: The conditional return type for SignInReturn is inverted:
change the conditional from Options extends { redirect: true } to Options
extends { redirect: false } so that the default (omitted redirect, which
defaults to true) yields void, while explicit redirect: false yields the data
object; apply the same inversion to the equivalent conditional types at the
other occurrences referenced (the similar conditionals around the blocks
currently at the later locations that return void vs data) so all SignIn-related
conditional return types use Options extends { redirect: false } instead of
Options extends { redirect: true } and update any related type aliases
accordingly (e.g., the other three matching conditional types in the file).

In `@packages/core/src/api/updateSession.ts`:
- Around line 15-66: The error branch's toResponse currently serializes an empty
body; update the error return in updateSession.ts (inside the catch of the
refreshSession call) so toResponse returns Response.json({ success: false,
redirectURL: null, error: { code, message } }, { status: 400, headers }) instead
of an empty object, and ensure the runtime error object (code/message) is the
one serialized. Also resolve the redirectURL type mismatch: make the success
return's redirectURL nullable (string | null) rather than forcing it to string —
adjust the UpdateSessionAPIReturn usage or the local return signature so
redirectURL is string | null (or alternatively always set redirectURL to ""),
and remove the misleading cast that hides the type mismatch.

In `@packages/core/src/client/client.ts`:
- Around line 141-166: The signOut function is forcing a default redirectTo of
"/" which overwrites server defaults; update the client.post call in signOut
(the function using getCSRFToken and client.post) to omit the redirectTo key
from searchParams when options?.redirectTo is undefined (only include redirectTo
when options?.redirectTo is explicitly provided), so the server receives no
redirectTo and can apply its configured default; leave token_type_hint and
X-CSRF-Token handling unchanged and keep the existing redirect logic that uses
json.redirectURL.

In `@packages/core/test/actions/signIn/signIn.test.ts`:
- Around line 367-371: The test assertion for the untrusted-origin sign-in
response is missing checks for standardized error metadata; update the
expect(await request.json()).toMatchObject({...}) in signIn.test.ts to include
an error object asserting error.code === 'UNTRUSTED_ORIGIN' and error.message
contains or equals the same message (e.g., error: { code: 'UNTRUSTED_ORIGIN',
message: expect.stringContaining('UNTRUSTED_ORIGIN') }) so the response's
error.code and error.message are validated alongside success, signInURL, and
redirect.

In `@packages/next/README.md`:
- Around line 103-112: The Profile component accesses session.user.name without
checking for a missing session; update the Profile function that calls
useSession() to handle the unauthenticated case after the loading state (i.e.,
when status !== "loading" but session is null/undefined). Add a null check for
session (or use optional chaining) and return a safe fallback UI such as a
sign-in prompt or a message like "Not signed in" instead of trying to read
session.user.name; ensure the change is made in the Profile component and
references the existing useSession, status === "loading", and session.user.name
usage.

In `@packages/next/src/`@types/api.ts:
- Around line 32-36: Update the NextSignInCredentials conditional type to match
the runtime implementation: change the constraint from SignInAPIOptions to
SignInCredentialsAPIOptions (import SignInCredentialsAPIOptions from
`@aura-stack/react/types`) and update the branch for Options extends { redirectTo:
string } to return never (not false) so the type reflects that redirect() throws
and does not return; keep the other branches unchanged.

In `@packages/next/src/lib/api.ts`:
- Around line 61-67: The handlers signIn, signInCredentials, and updateSession
currently only redirect when both signIn.success and options?.redirectTo are
truthy, which breaks the previous default automatic redirect; change the logic
so that if options?.redirect === false you return the raw result, otherwise if
signIn.success perform a redirect using options.redirectTo if provided, falling
back to the server-provided URL (e.g., signIn.signInURL or equivalent), and then
return the result when not redirecting; update the conditional in the signIn,
signInCredentials, and updateSession flows to implement this (check
options?.redirect first, then if signIn.success call redirect(options.redirectTo
?? signIn.signInURL)).
- Around line 71-86: The generic Options in signInCredentials is never inferred
because the parameter is typed as the constraint type; change the inner function
signature in signInCredentials to accept options: Options (not
SignInCredentialsAPIOptions) so TypeScript can infer the caller's literal type
and preserve the conditional NextSignInCredentials<Options> return narrowing;
update any usages inside (e.g., payload: options.payload and
options?.redirectTo) to remain compatible with Options.

In `@packages/react-router/README.md`:
- Around line 91-99: The example Profile component does not guard against a null
session after loading; update the Profile component that uses useSession so it
checks session (or status === "authenticated") before accessing
session.user.name and render a safe fallback (e.g., a sign-in prompt or null)
when session is null/unauthenticated to avoid runtime errors and TypeScript
failures.
- Line 116: Remove the stray standalone closing code fence (the lone ``` at EOF)
from the README so it no longer opens an unterminated Markdown code block;
locate the trailing triple-backtick token and delete it, leaving the footer and
document tail as plain Markdown.

In `@packages/react-router/src/`@types/index.ts:
- Around line 64-66: Replace the incorrect session field type in the exported
ReactRouterUpdateSessionAPIOptions type: the session currently uses
UpdateSessionOptions<DefaultUser> (client-side hook options) but must be the
core session shape. Update the definition of ReactRouterUpdateSessionAPIOptions
to use DeepPartial<Session<DefaultUser>> (or equivalently
UpdateSessionAPIOptions<DefaultUser>["session"]) for the session property so
server-side callers can pass the actual partial session object; keep the rest of
the type (Partial<UpdateSessionAPIOptions<DefaultUser>> & { request: Request })
the same and only change the session property's type in the type alias.

In `@packages/react-router/src/lib/api.ts`:
- Around line 57-83: In updateSession and signOut, callers can override the
injected headers because you spread ...options after setting headers; change the
call to construct the payload by spreading options first (but excluding the
request property) and then explicitly set headers from options.request.headers
so the injected headers cannot be overridden, and stop forwarding the request
object to api.updateSession/api.signOut (i.e., build an args object that omits
request, spreads remaining options, then sets headers: options.request.headers);
update the functions updateSession and signOut accordingly.

In `@packages/react/src/hooks.ts`:
- Around line 65-68: The JSDoc above useUpdateSession is stale/garbled: update
the comment to fix spacing and wording and remove reference to removed
skipRefresh option; instead document the current behavior and options (e.g.,
mention that redirect: false can be used to avoid a follow-up getSession),
correct the missing noun after "Default", and make the summary clear that this
patches session user/expiry and merges fields per call; update the block above
the useUpdateSession function accordingly.

---

Outside diff comments:
In `@packages/core/src/api/credentials.ts`:
- Around line 49-78: The catch block in the credentials sign-in flow sets
generic code/message because isAuthInternalError(error) doesn't recognize
AuthValidationError; update the catch to extract error.code and error.message
for AuthValidationError as well (or use a type-guard that checks for both
structures) before building the SignInCredentialsAPIReturn; specifically adjust
the logic around isAuthInternalError(error) in the catch so it also handles
error instanceof AuthValidationError (or a shared hasCodeAndMessage guard) and
assigns code = error.code and message = error.message prior to creating
invalidCredentials, keeping the existing logger branches (logger?.log(...))
intact.

In `@packages/core/src/api/getSession.ts`:
- Around line 16-24: The toResponse() for the unauthorized GetSessionAPIReturn
in getSession.ts currently returns { success:false, session:null } only; update
the unauthorized object's toResponse implementation (the toResponse method on
the unauthorized constant) to include the error metadata (error.code and
error.message and/or the entire error object) in the JSON body so the HTTP 401
response shape matches the returned object (e.g., return Response.json({
success: false, session: null, error }, { status: 401, headers })).

In `@packages/core/src/client/client.ts`:
- Around line 111-139: The updateSession function dereferences options.session
without guarding against missing/invalid input; add an early validation at the
top of updateSession (the function named updateSession and the type
UpdateSessionOptions) that checks whether options and options.session exist and
are proper objects, and if not return the expected UpdateSessionReturn shaped
failure (e.g. { success: false, session: null } or include an error field)
instead of continuing to destructure; update the destructuring to use a safe
access pattern (avoid const { session } = options when options may be undefined)
and log or throw an AuthClientError consistent with other error paths so callers
get the typed error shape immediately.

In `@packages/core/test/actions/signOut/signOut.test.ts`:
- Around line 145-184: The tests "valid sessionToken cookie with redirectTo
parameter" and "valid sessionToken cookie with invalid redirectTo parameter"
currently assert a 302 status but also assert the JSON body has redirect: false,
which is contradictory; update these tests to reflect that a 302 response should
report redirect: true (modify the assertions in those two tests to expect
redirect: true and keep the redirectURL checks) and ensure the first test still
asserts the Location header equals "/custom-logout-page"; alternatively, if the
intended behavior is no HTTP redirect, change the response status to 200 in the
code under test and keep redirect: false—adjust whichever side (test assertions
or response status in the signOut handler) to make the status, Location header,
and JSON redirect flag consistent.

---

Nitpick comments:
In `@apps/react-router/app/lib/auth.ts`:
- Around line 14-25: The authorize function in the credentials object uses
username.toLocaleLowerCase() when building the email which can yield
locale-specific results; change that call to username.toLowerCase() in the
authorize implementation (the block that returns { sub, name: username, email:
`${username.toLocaleLowerCase()}@example.com` }) so the email is normalized in
an ASCII-safe way; no other behavior changes needed (leave createSecretValue and
returned fields intact).

In `@packages/next/README.md`:
- Around line 79-93: The example for createAuthClient/AuthProvider lacks
TypeScript typings for the component props; update the App component to use
explicit types for children (e.g., React.ReactNode) and annotate the client
variable with the proper auth client type returned by createAuthClient so the
snippet demonstrates type-safe usage; also replace the placeholder "/* your
config */" with either a minimal config example or a short comment pointing to
the configuration docs to clarify what belongs there.

In `@packages/next/src/lib/api.ts`:
- Around line 105-117: The public parameter type for signOut was overly widened
to Partial<Options>, which makes required fields (like headers in
SignOutAPIOptions) optional and breaks inference for redirect/redirectTo and the
conditional NextSignOutReturn; update the signOut parameter type in the signOut
function so only headers are optional—e.g. use Omit<Options, "headers"> & {
headers?: HeadersInit } or Partial<Pick<Options, "headers">> & Omit<Options,
"headers">—so the generic Options remains useful, keep the body using
api.signOut({ headers: await headers(), ...options }) and preserve
applyCookies/redirect behavior.

In `@packages/react-router/src/lib/api.ts`:
- Around line 42-55: In signInCredentials (the exported function) remove the
redundant payload re-assignment inside the call to api.signInCredentials —
replace the object spread { ...options, payload: options.payload } with simply {
...options } so you pass the same options without the no-op override; update the
call site at api.signInCredentials(...) accordingly in the signInCredentials
function.

In `@packages/react-router/tsdown.config.ts`:
- Around line 16-19: The current override of deps replaces the shared
tsdownConfig.deps entirely; update the override to defensively merge the nested
deps (e.g., set deps: { ...tsdownConfig.deps, onlyBundle: false, neverBundle:
["react","react-dom","react-router"] }) so any future fields added to the shared
tsdownConfig.deps are preserved; reference the top-level tsdownConfig and the
deps object when making this 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: e05f8e27-b6bf-401c-9b2f-635dd8eae16d

📥 Commits

Reviewing files that changed from the base of the PR and between f6a13c7 and 03971aa.

📒 Files selected for processing (44)
  • apps/astro/src/lib/server.ts
  • apps/nextjs/app-router/src/app/globals.css
  • apps/nuxt/app/assets/css/tailwind.css
  • apps/nuxt/app/components/header.vue
  • apps/react-router/app/lib/auth.ts
  • apps/react-router/app/routes.ts
  • apps/react-router/app/routes/api.auth.$.tsx
  • apps/react-router/app/routes/client/index.tsx
  • apps/react-router/app/routes/server/index.tsx
  • packages/core/CHANGELOG.md
  • packages/core/README.md
  • packages/core/src/@types/api.ts
  • packages/core/src/@types/index.ts
  • packages/core/src/@types/session.ts
  • packages/core/src/api/createApi.ts
  • packages/core/src/api/credentials.ts
  • packages/core/src/api/getSession.ts
  • packages/core/src/api/signIn.ts
  • packages/core/src/api/signOut.ts
  • packages/core/src/api/updateSession.ts
  • packages/core/src/client/client.ts
  • packages/core/src/shared/identity.ts
  • packages/core/test/actions/signIn/signIn.test.ts
  • packages/core/test/actions/signOut/signOut.test.ts
  • packages/core/test/actions/updateSession/updateSession.test.ts
  • packages/core/test/api/signIn.test.ts
  • packages/core/test/api/signOut.test.ts
  • packages/core/test/api/updateSession.test.ts
  • packages/core/test/types.test-d.ts
  • packages/next/CHANGELOG.md
  • packages/next/README.md
  • packages/next/src/@types/api.ts
  • packages/next/src/lib/api.ts
  • packages/react-router/CHANGELOG.md
  • packages/react-router/README.md
  • packages/react-router/src/@types/index.ts
  • packages/react-router/src/lib/api.ts
  • packages/react-router/tsdown.config.ts
  • packages/react/CHANGELOG.md
  • packages/react/README.md
  • packages/react/src/@types/types.ts
  • packages/react/src/context.tsx
  • packages/react/src/hooks.ts
  • packages/react/test/hooks.test.tsx
✅ Files skipped from review due to trivial changes (9)
  • packages/next/CHANGELOG.md
  • apps/nuxt/app/components/header.vue
  • packages/core/README.md
  • packages/core/test/types.test-d.ts
  • packages/react/CHANGELOG.md
  • packages/react/README.md
  • packages/core/src/api/createApi.ts
  • packages/core/test/actions/updateSession/updateSession.test.ts
  • packages/react-router/CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/nuxt/app/assets/css/tailwind.css
  • apps/nextjs/app-router/src/app/globals.css
  • packages/core/test/api/signOut.test.ts
  • packages/core/src/api/signIn.ts
  • apps/react-router/app/routes/client/index.tsx
  • apps/react-router/app/routes.ts

Comment thread apps/react-router/app/routes/api.auth.$.tsx
Comment thread apps/react-router/app/routes/server/index.tsx
Comment thread apps/react-router/app/routes/server/index.tsx
Comment thread packages/core/CHANGELOG.md Outdated
Comment thread packages/core/src/@types/api.ts
Comment thread packages/react-router/README.md
Comment thread packages/react-router/README.md Outdated
Comment thread packages/react-router/src/@types/index.ts
Comment thread packages/react-router/src/lib/api.ts
Comment thread packages/react/src/hooks.ts
@halvaradop halvaradop added enhancement New feature or request feature New functionality chore Maintenance tasks, tooling, dependency updates, or housekeeping changes. refactor Refactor without changing behavior examples Example code, demos, or sample implementations. labels Apr 20, 2026
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: 9

♻️ Duplicate comments (3)
apps/nextjs/app-router/src/app/client/page.tsx (1)

8-8: ⚠️ Potential issue | 🔴 Critical

Still importing non-existent SubmitEvent from react (flagged in a previous review).

This was raised on the prior commit but remains unaddressed. Use FormEvent<HTMLFormElement> (or switch to SubmitEventHandler for the handler type).

Proposed fix
-import type { SubmitEvent } from "react"
+import type { FormEvent } from "react"
@@
-    const handleSignInCredentials = async (event: SubmitEvent<HTMLFormElement>) => {
+    const handleSignInCredentials = async (event: FormEvent<HTMLFormElement>) => {

Also applies to: 14-14

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

In `@apps/nextjs/app-router/src/app/client/page.tsx` at line 8, The import is
using a non-existent SubmitEvent from React; replace it with the correct React
types and update any handler signatures that reference it (e.g., change import
type { SubmitEvent } from "react" to import type { FormEvent } from "react" and
use FormEvent<HTMLFormElement> for the form handler parameter, or alternatively
use SubmitEventHandler<HTMLFormElement> as the handler type); locate and update
the handler function(s) (e.g., handleSubmit) and their parameter annotations to
use the chosen correct type.
apps/nextjs/pages-router/src/pages/client/index.tsx (1)

6-6: ⚠️ Potential issue | 🔴 Critical

SubmitEvent is not exported by react — same issue as apps/react-router/app/routes/server/index.tsx and apps/nextjs/app-router/src/app/client/page.tsx. Use FormEvent<HTMLFormElement> instead.

Proposed fix
-import type { SubmitEvent } from "react"
+import type { FormEvent } from "react"
@@
-    const handleSignInCredentials = async (event: SubmitEvent<HTMLFormElement>) => {
+    const handleSignInCredentials = async (event: FormEvent<HTMLFormElement>) => {

Also applies to: 12-12

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

In `@apps/nextjs/pages-router/src/pages/client/index.tsx` at line 6, Replace the
invalid import of SubmitEvent from "react" with the correct type
FormEvent<HTMLFormElement>; remove the line importing SubmitEvent and update any
usage sites (e.g., onSubmit handlers or functions named handleSubmit /
submitForm in this file) to type their event parameter as
FormEvent<HTMLFormElement> instead of SubmitEvent so the code compiles and uses
the supported React type.
packages/next/src/lib/api.ts (1)

61-67: ⚠️ Potential issue | 🟠 Major

Default-redirect behavior still requires explicit redirectTo — contradicts the JSDoc.

Previously flagged and still present across all three handlers: the redirect fires only when options?.redirectTo is truthy. Callers invoking with no options (the previous "default redirect" path) — and, notably, the demo pages calling signOut() bare (e.g., apps/nextjs/app-router/src/app/client/page.tsx line 40) — now silently get the raw data object instead of a navigation. This also directly contradicts the JSDoc block added at lines 127–130: "By default the redirect is automatic, but it can be disabled by setting the redirect option to false."

Recommend restoring the default: redirect whenever redirect !== false and a redirect URL is available (falling back to the server-provided URL).

Proposed fix for signIn (apply same pattern to signInCredentials and updateSession)
-        if (options?.redirect === false) {
-            return signIn as NextSignInReturn<Options>
-        }
-        if (signIn.success && options?.redirectTo) {
-            return redirect(signIn.signInURL)
-        }
-        return signIn as NextSignInReturn<Options>
+        if (options?.redirect !== false && signIn.success && signIn.signInURL) {
+            return redirect(signIn.signInURL)
+        }
+        return signIn as NextSignInReturn<Options>

Also applies to: 79-82, 96-99

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

In `@packages/next/src/lib/api.ts` around lines 61 - 67, The redirect logic
currently only fires when options.redirectTo is provided, contradicting the
JSDoc; update the handlers (signIn, signInCredentials, updateSession) so they
perform a redirect whenever signIn.success (or equivalent result.success) is
true and options.redirect !== false, using options.redirectTo if present
otherwise falling back to the server-provided URL (e.g., signIn.signInURL); if a
resolved URL exists call redirect(url) and otherwise return the original
response object. Ensure you change the condition surrounding redirect in the
signIn, signInCredentials and updateSession handlers (the blocks that currently
check options?.redirectTo) to the new logic.
🧹 Nitpick comments (3)
packages/core/src/client/client.ts (1)

111-142: updateSession fetches a CSRF token before checking whether there's anything to update.

When options.session is missing the function returns { success: false, session: null }, but only after a full network round-trip to /csrfToken. Hoisting the early-return above getCSRFToken() avoids the wasted request and makes the no-op path obvious.

🔧 Proposed fix
     const updateSession = async <Options extends UpdateSessionOptions<DefaultUser>>(
         options: Options
     ): Promise<UpdateSessionReturn<Options, DefaultUser>> => {
         try {
+            const { session } = options ?? {}
+            if (!session) {
+                return { success: false, session: null } as UpdateSessionReturn<Options, DefaultUser>
+            }
             const csrfToken = await getCSRFToken()
             if (!csrfToken) {
                 throw new AuthClientError("Failed to fetch CSRF token for session update.")
             }
-            const { session } = options ?? {}
-            if (!session) {
-                return { success: false, session: null } as UpdateSessionReturn<Options, DefaultUser>
-            }
             const user = session.user ?? {}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/client/client.ts` around lines 111 - 142, The updateSession
function currently calls getCSRFToken() before checking options.session, causing
an unnecessary network request; reorder the logic in updateSession so you first
extract const { session } = options ?? {} and return { success: false, session:
null } as UpdateSessionReturn<Options, DefaultUser> if !session, and only then
call getCSRFToken() and proceed with client.patch("/session") and the rest of
the flow (preserving existing error handling and redirect logic).
packages/next/src/lib/api.ts (1)

73-77: Redundant key after ...options spread.

payload: options.payload on line 76 (and session: options.session on line 92) duplicates what ...options already carries. Harmless, but noisy; dropping the explicit keys makes the forwarding intent clear.

         const signIn = await api.signInCredentials({
             headers: await headers(),
             ...options,
-            payload: options.payload,
         })
@@
         const updated = await api.updateSession({
             ...options,
-            session: options.session,
             headers: await headers(),
         })

Also applies to: 90-94

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

In `@packages/next/src/lib/api.ts` around lines 73 - 77, Remove the redundant
explicit properties that duplicate the spreaded options: in the call to
api.signInCredentials (the signIn variable) drop the explicit payload:
options.payload because ...options already includes it; likewise remove session:
options.session in the other API call that spreads ...options (the call around
line 92) so the forwarded options are not repeated and the intent to forward all
options via the spread is clear.
apps/nextjs/app-router/src/app/client/page.tsx (1)

44-44: Inconsistent container height utility with sibling demo pages.

Sibling pages (apps/nextjs/pages-router/.../client/index.tsx, apps/nuxt/app/pages/client.vue, apps/react-router/.../server/index.tsx) all use min-h-container, but this page uses min-h-[calc(100vh-3.8rem)] mt-[3.8rem]. If min-h-container was introduced as the shared utility for this exact purpose (per the AI summary), prefer it here for consistency.

-        <main className="w-11/12 min-h-[calc(100vh-3.8rem)] max-w-5xl mt-[3.8rem] mx-auto content-center">
+        <main className="w-11/12 min-h-container max-w-5xl mx-auto content-center">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/app-router/src/app/client/page.tsx` at line 44, The main
element's className uses custom min-h and top margin utilities instead of the
shared utility; update the <main> element (className on the main tag in
page.tsx) to replace "min-h-[calc(100vh-3.8rem)] mt-[3.8rem]" with the shared
"min-h-container" utility so it matches sibling demo pages and keeps consistent
layout behavior across apps.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/nuxt/app/composables/useAuthClient.ts`:
- Line 45: The signInCredentials function is typed incorrectly: it accepts
options?: SignInOptions (OAuth sign-in) but builds an object with payload:
credentials for the credentials flow; change the options parameter type to
Omit<SignInCredentialsOptions, "payload"> so callers must provide the correct
credential-flow options shape and the function can safely spread options and add
payload; update the function signature for signInCredentials and any related
callers/exports to use Omit<SignInCredentialsOptions, "payload"> and ensure
payload remains the credentials object.

In `@apps/nuxt/app/pages/client.vue`:
- Around line 23-30: handleUpdateSession is currently a no-op because its body
is commented out; restore and wire it to call updateSession with the correct
payload: read username and email from the FormData (formData.get("username"),
formData.get("email")), build the UpdateSessionOptions-shaped payload under
session.user (matching UpdateSessionOptions/session.user), await
updateSession(...) inside handleUpdateSession, handle/await errors and update
local UI state or refetch the session as needed so the EditProfile flow actually
updates on the client; reference the function name handleUpdateSession and the
updateSession API used in server.vue/nextjs example to match shape.

In `@apps/react-router/app/components/edit-profile.tsx`:
- Around line 14-28: The label element currently uses htmlFor="signout" which
points to a non-existent id and is inappropriate because the adjacent control is
a Button; remove the htmlFor attribute (or replace the <label> with a non-label
element) in the Edit Profile block so the text is not tied to a missing form
control—edit the <label> with text "Edit Profile" (or convert it to a <span> or
<p>) near the Button component and keep the Button (data-open={isOpen},
onClick={() => setIsOpen(true)}) unchanged to preserve behavior.

In `@apps/react-router/app/routes/server/index.tsx`:
- Line 5: The import of SubmitEvent from "react" is invalid; replace that import
and any type annotations using SubmitEvent with a React-compatible event type:
import/remove the incorrect SubmitEvent import and update handlers/annotations
(e.g., the function or variable named in the file that currently types
parameters as SubmitEvent) to use FormEvent<HTMLFormElement> from "react" or use
the DOM global SubmitEvent by typing the parameter as SubmitEvent and casting
currentTarget as HTMLFormElement where needed; ensure to update both occurrences
(the import at the top and the second occurrence around the handler at line ~69)
so types compile correctly.

In `@apps/tanstack-start/src/lib/auth.ts`:
- Around line 11-22: The authorize implementation currently creates a new random
sub on every sign-in and accepts any non-empty username/password; update
authorize to (1) be marked with a clear "// demo only" comment, (2) stop
generating a random identity each call by deriving a stable sub from the
username (e.g., hash or deterministic encoding of username) instead of
createSecretValue(10) so the same credentials map to the same user, and (3) add
a simple placeholder credential check (e.g., compare against a hard-coded demo
password or call a stubbed verifyCredentials function) to make the acceptance
behavior explicit; refer to the authorize function and the createSecretValue(10)
usage when applying these changes.

In `@apps/tanstack-start/src/routes/server.tsx`:
- Around line 100-110: The sign-out UI incorrectly uses a <form> as a layout
container and a <label htmlFor="signout"> that points to no control; replace the
<form> with a non-semantic container (e.g., a div) so Enter-key submissions
can't occur, and change the <label htmlFor="signout"> to a plain text element
(e.g., a div/span) or associate it with a real control id if you intend it to be
a form label; keep the Button (type="button") and its onClick={handleSignOut}
intact so the existing handleSignOut handler continues to run.
- Around line 78-92: The img alt value can end up undefined or "null" because
session.user.name is string | null | undefined; update the <img> usage (the
block that checks session?.user?.image and renders an img) to always pass a
non-null string by coalescing session?.user?.name into a sensible default (e.g.,
"User avatar" or "Unknown user") so alt never receives null/undefined; locate
the img element that uses session.user.image and change its alt expression
accordingly.

In `@packages/core/src/api/updateSession.ts`:
- Around line 23-46: The code computes redirectURL regardless of whether refresh
produced a session; change the logic in updateSession (around redirectURL,
redirectToInit, requestInit, and createRedirectTo) so you only call
createRedirectTo and set redirectURL when session is non-null (i.e., after
refreshSession succeeded). Ensure the returned object and toResponse continue to
reflect success based on session and that redirectURL remains null when session
is null.

In `@packages/core/test/actions/session/session.test.ts`:
- Around line 49-56: The test is incorrectly asserting an error object on the
success branch of the GetSessionAPIReturn discriminated union; update the test
expectations in session.test.ts (the expect(await request.json()) assertions
around lines showing success true) to only assert { success: true, session: {
user: sessionPayload, expires: expect.any(String) } } and remove the error
object check, and ensure packages/core/src/api/getSession.ts continues to return
the strict discriminated union (success branch must not include an error field)
so tests and implementation remain consistent.

---

Duplicate comments:
In `@apps/nextjs/app-router/src/app/client/page.tsx`:
- Line 8: The import is using a non-existent SubmitEvent from React; replace it
with the correct React types and update any handler signatures that reference it
(e.g., change import type { SubmitEvent } from "react" to import type {
FormEvent } from "react" and use FormEvent<HTMLFormElement> for the form handler
parameter, or alternatively use SubmitEventHandler<HTMLFormElement> as the
handler type); locate and update the handler function(s) (e.g., handleSubmit)
and their parameter annotations to use the chosen correct type.

In `@apps/nextjs/pages-router/src/pages/client/index.tsx`:
- Line 6: Replace the invalid import of SubmitEvent from "react" with the
correct type FormEvent<HTMLFormElement>; remove the line importing SubmitEvent
and update any usage sites (e.g., onSubmit handlers or functions named
handleSubmit / submitForm in this file) to type their event parameter as
FormEvent<HTMLFormElement> instead of SubmitEvent so the code compiles and uses
the supported React type.

In `@packages/next/src/lib/api.ts`:
- Around line 61-67: The redirect logic currently only fires when
options.redirectTo is provided, contradicting the JSDoc; update the handlers
(signIn, signInCredentials, updateSession) so they perform a redirect whenever
signIn.success (or equivalent result.success) is true and options.redirect !==
false, using options.redirectTo if present otherwise falling back to the
server-provided URL (e.g., signIn.signInURL); if a resolved URL exists call
redirect(url) and otherwise return the original response object. Ensure you
change the condition surrounding redirect in the signIn, signInCredentials and
updateSession handlers (the blocks that currently check options?.redirectTo) to
the new logic.

---

Nitpick comments:
In `@apps/nextjs/app-router/src/app/client/page.tsx`:
- Line 44: The main element's className uses custom min-h and top margin
utilities instead of the shared utility; update the <main> element (className on
the main tag in page.tsx) to replace "min-h-[calc(100vh-3.8rem)] mt-[3.8rem]"
with the shared "min-h-container" utility so it matches sibling demo pages and
keeps consistent layout behavior across apps.

In `@packages/core/src/client/client.ts`:
- Around line 111-142: The updateSession function currently calls getCSRFToken()
before checking options.session, causing an unnecessary network request; reorder
the logic in updateSession so you first extract const { session } = options ??
{} and return { success: false, session: null } as UpdateSessionReturn<Options,
DefaultUser> if !session, and only then call getCSRFToken() and proceed with
client.patch("/session") and the rest of the flow (preserving existing error
handling and redirect logic).

In `@packages/next/src/lib/api.ts`:
- Around line 73-77: Remove the redundant explicit properties that duplicate the
spreaded options: in the call to api.signInCredentials (the signIn variable)
drop the explicit payload: options.payload because ...options already includes
it; likewise remove session: options.session in the other API call that spreads
...options (the call around line 92) so the forwarded options are not repeated
and the intent to forward all options via the spread is clear.
🪄 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: 45ef0285-9799-42db-9f81-fe4194e64640

📥 Commits

Reviewing files that changed from the base of the PR and between 03971aa and c77d28b.

📒 Files selected for processing (48)
  • apps/nextjs/app-router/next.config.ts
  • apps/nextjs/app-router/src/app/client/page.tsx
  • apps/nextjs/app-router/src/app/layout.tsx
  • apps/nextjs/app-router/src/app/server/page.tsx
  • apps/nextjs/app-router/src/components/edit-profile.tsx
  • apps/nextjs/app-router/src/lib/auth.ts
  • apps/nextjs/pages-router/next.config.ts
  • apps/nextjs/pages-router/src/components/edit-profile.tsx
  • apps/nextjs/pages-router/src/lib/auth.ts
  • apps/nextjs/pages-router/src/pages/client/index.tsx
  • apps/nextjs/pages-router/src/pages/server/index.tsx
  • apps/nuxt/app/components/header.vue
  • apps/nuxt/app/composables/useAuthClient.ts
  • apps/nuxt/app/pages/client.vue
  • apps/nuxt/app/pages/index.vue
  • apps/react-router/app/components/auth-client.tsx
  • apps/react-router/app/components/edit-profile.tsx
  • apps/react-router/app/lib/auth.ts
  • apps/react-router/app/routes/api.auth.$.tsx
  • apps/react-router/app/routes/client/index.tsx
  • apps/react-router/app/routes/server/index.tsx
  • apps/tanstack-start/src/components/auth-client.tsx
  • apps/tanstack-start/src/components/auth-server.tsx
  • apps/tanstack-start/src/components/edit-profile.tsx
  • apps/tanstack-start/src/components/footer.tsx
  • apps/tanstack-start/src/lib/auth-server.ts
  • apps/tanstack-start/src/lib/auth.ts
  • apps/tanstack-start/src/routes/client.tsx
  • apps/tanstack-start/src/routes/server.tsx
  • packages/core/CHANGELOG.md
  • packages/core/src/@types/api.ts
  • packages/core/src/api/credentials.ts
  • packages/core/src/api/getSession.ts
  • packages/core/src/api/signIn.ts
  • packages/core/src/api/updateSession.ts
  • packages/core/src/client/client.ts
  • packages/core/src/shared/errors.ts
  • packages/core/test/actions/session/session.test.ts
  • packages/core/test/actions/signIn/signIn.test.ts
  • packages/next/README.md
  • packages/next/src/@types/api.ts
  • packages/next/src/lib/api.ts
  • packages/react-router/README.md
  • packages/react-router/src/@types/index.ts
  • packages/react-router/src/lib/api.ts
  • packages/react-router/tsdown.config.ts
  • packages/react/README.md
  • packages/react/src/hooks.ts
💤 Files with no reviewable changes (3)
  • apps/tanstack-start/src/components/auth-client.tsx
  • apps/react-router/app/components/auth-client.tsx
  • apps/tanstack-start/src/components/auth-server.tsx
✅ Files skipped from review due to trivial changes (10)
  • apps/nextjs/app-router/src/app/layout.tsx
  • apps/nextjs/pages-router/next.config.ts
  • apps/nextjs/app-router/next.config.ts
  • packages/core/test/actions/signIn/signIn.test.ts
  • apps/nuxt/app/components/header.vue
  • packages/react/README.md
  • packages/react-router/README.md
  • packages/core/src/api/signIn.ts
  • packages/core/CHANGELOG.md
  • packages/core/src/@types/api.ts
🚧 Files skipped from review as they are similar to previous changes (19)
  • apps/react-router/app/routes/api.auth.$.tsx
  • packages/react-router/tsdown.config.ts
  • apps/nextjs/app-router/src/lib/auth.ts
  • apps/nextjs/pages-router/src/lib/auth.ts
  • apps/nextjs/app-router/src/components/edit-profile.tsx
  • apps/tanstack-start/src/components/footer.tsx
  • apps/nextjs/pages-router/src/components/edit-profile.tsx
  • packages/core/src/api/credentials.ts
  • apps/tanstack-start/src/components/edit-profile.tsx
  • apps/nextjs/pages-router/src/pages/server/index.tsx
  • apps/react-router/app/routes/client/index.tsx
  • apps/nextjs/app-router/src/app/server/page.tsx
  • packages/next/README.md
  • packages/react-router/src/@types/index.ts
  • packages/core/src/api/getSession.ts
  • apps/tanstack-start/src/routes/client.tsx
  • packages/react/src/hooks.ts
  • apps/tanstack-start/src/lib/auth-server.ts
  • packages/react-router/src/lib/api.ts

Comment thread apps/nuxt/app/composables/useAuthClient.ts Outdated
Comment thread apps/nuxt/app/pages/client.vue
Comment thread apps/react-router/app/components/edit-profile.tsx
Comment thread apps/react-router/app/routes/server/index.tsx
Comment thread apps/tanstack-start/src/lib/auth.ts
Comment thread apps/tanstack-start/src/routes/server.tsx
Comment thread apps/tanstack-start/src/routes/server.tsx Outdated
Comment thread packages/core/src/api/updateSession.ts
Comment thread packages/core/test/actions/session/session.test.ts
@halvaradop halvaradop merged commit 5a6b756 into master Apr 20, 2026
7 checks passed
@halvaradop halvaradop deleted the chore/update-example-ui branch April 20, 2026 20:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Maintenance tasks, tooling, dependency updates, or housekeeping changes. enhancement New feature or request examples Example code, demos, or sample implementations. feature New functionality refactor Refactor without changing behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant