Skip to content

feat(core): introduce api object via createAuth#112

Merged
halvaradop merged 8 commits intomasterfrom
feat/introduce-server-api
Mar 6, 2026
Merged

feat(core): introduce api object via createAuth#112
halvaradop merged 8 commits intomasterfrom
feat/introduce-server-api

Conversation

@halvaradop
Copy link
Member

@halvaradop halvaradop commented Mar 3, 2026

Description

This pull request introduces the api object returned by the createAuth function, enabling direct interaction with Aura Auth server capabilities without issuing HTTP requests to mounted endpoints. The new API provides built-in utilities to retrieve and sign out sessions programmatically on the server, eliminating the need to call the /session or /signOut endpoints using fetch or Request.

This improvement simplifies server-side integrations and enhances performance by avoiding unnecessary network calls while maintaining the same authentication logic used by the HTTP handlers.

This PR was motivated by the integration examples included in the repository. Across the 15 integrations, many of them implemented nearly identical logic to retrieve session data or execute authentication actions from both server and client contexts. In most cases, these interactions were performed using fetch requests to the same server where Aura Auth was mounted. While this approach works correctly, it introduces unnecessary internal requests and makes the flow less transparent, since the server ends up making HTTP requests or redirects to itself.

To address this, the new api utilities allow the server to interact directly with the internal logic that powers the endpoints, instead of calling the endpoints themselves. This removes the need to construct Request objects or perform fetch calls when the execution already occurs within the same runtime.

Additionally, these utilities are inherently trusted because they operate within the same origin and runtime context as the mounted server. As a result, there is no need to introduce additional validation layers that would normally be required for external HTTP requests.

This PR is specifically focused on improving the server-side developer experience, reducing duplicated code across integrations, and eliminating unnecessary request overhead.

export const { handlers, jose, api }: AuthInstance = createAuth({
    oauth,
    trustedOrigins: ["http://localhost:3000", "https://*.vercel.app"],
})


export const getSession = async (): Promise<Session | null> => {
    try {
        const session = await api.getSession({
            headers: await headers(),
        })
        if (!session.authenticated) {
            return null
        }
        return session.session
    } catch {
        console.log("[error:server] getSession - Failed to retrieve session")
        return null
    }
}

The api interface enables server-side access to authentication utilities while keeping the implementation consistent with the existing internal flows used by Aura Auth handlers.

@vercel
Copy link

vercel bot commented Mar 3, 2026

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

Project Deployment Actions Updated (UTC)
auth-nextjs-demo Ready Ready Preview, Comment Mar 6, 2026 1:08am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
auth Skipped Skipped Mar 6, 2026 1:08am

@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This pull request introduces a new centralized API surface (api) exported from createAuth across all framework integrations, replacing individual getSession utilities with a unified api.getSession({ headers }) call pattern. The core refactoring includes new API factory functions, updated session response formats with explicit authenticated boolean flags, and simplified session retrieval logic across multiple applications.

Changes

Cohort / File(s) Summary
Auth Module API Exports
apps/*/src/auth.ts (bun, deno, elysia, express, hono, oak, supabase, astro, cloudflare, nextjs, react-router, tanstack-start)
Added api to the destructured AuthInstance export from createAuth(), expanding public properties from { handlers, jose } to { handlers, jose, api }.
Removed Local getSession Utilities
apps/*/src/lib/get-session.ts, apps/nextjs/pages-router/src/lib/server.ts, apps/supabase/functions/_shared/get-session.ts
Deleted entire `getSession(request: Request
Framework-Specific Session Retrieval Updates
apps/bun/src/index.ts, apps/deno/src/index.ts, apps/elysia/src/plugins/with-auth.ts, apps/express/src/lib/verify-session.ts, apps/hono/src/middleware/with-auth.ts, apps/oak/src/middleware/with-auth.ts, apps/supabase/functions/auth/index.ts
Replaced local getSession(request) calls with api.getSession({ headers: request.headers }) and changed session validation from truthiness/session.user checks to session.authenticated checks.
Next.js App Router Session Updates
apps/nextjs/app-router/src/lib/server.ts, apps/nextjs/app-router/src/lib/client.ts, apps/nextjs/pages-router/src/pages/index.tsx
Updated session retrieval to use api.getSession() instead of internal client calls; changed sign-in flow to use client-side redirect pattern; adjusted cookie manipulation for sign-out.
React Router & TanStack Server Actions
apps/react-router/app/actions/auth.server.ts, apps/tanstack-start/src/lib/auth-server.ts
Replaced CSRF-token-based sign-out with api.signOut({ redirectTo, headers }); updated session validation to check session.authenticated.
Astro Client & Server Updates
apps/astro/src/lib/server.ts, apps/astro/src/lib/client.ts
Migrated session retrieval to api.getSession() and updated sign-out to use api.signOut(); changed session validation predicates to session.authenticated.
Core API Factory
packages/core/src/api/createApi.ts, packages/core/src/api/getSession.ts, packages/core/src/api/signOut.ts
New modules introducing createAPI() factory that exposes getSession() and signOut() methods for server-side session management without calling mounted endpoints; includes CSRF validation and structured error handling.
Core Context & Infrastructure
packages/core/src/context.ts, packages/core/src/index.ts, packages/core/src/cookie.ts, packages/core/src/utils.ts
Added createContext() function for centralized configuration; broadened getCookie() and useSecureCookies() to accept Request | Headers; added validateRedirectTo() utility; integrated new API layer into createAuth() return value.
Core Type System
packages/core/src/@types/index.ts
Added SessionResponse, GetSessionAPI, and AuthAPI types; extended RouterGlobalContext and AuthInstance interfaces with new api: AuthAPI property.
Session Action & Core Logic
packages/core/src/actions/session/session.ts, packages/core/src/actions/signOut/signOut.ts
Refactored session endpoint to use centralized getSession() helper and return nested { session, authenticated } format; simplified sign-out action to delegate to new signOut() helper with context-driven security checks.
Test Updates
packages/core/test/actions/session/session.test.ts, apps/express/test/index.test.ts, packages/core/test/cookie.test.ts
Updated test assertions to reflect new SessionResponse format with authenticated and nested session fields; added test for getCookie() with Headers input.
Documentation & Configuration
packages/core/CHANGELOG.md, packages/core/.vscode/settings.json, apps/nextjs/app-router/src/app/profile/page.tsx
Added changelog entry for new api surface; created VSCode settings placeholder; added new profile page component.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client/Request
    participant App as Application
    participant API as api.getSession()
    participant SessionAction as Session Action
    participant Cookies as Cookie Store
    participant JWT as Jose JWT

    Client->>App: POST /api/protected
    App->>API: api.getSession({ headers })
    API->>SessionAction: getSession({ ctx, headers })
    SessionAction->>Cookies: getCookie(headers, sessionToken)
    Cookies-->>SessionAction: token string
    SessionAction->>JWT: decodeJWT(token)
    JWT-->>SessionAction: { user, exp, ...claims }
    SessionAction-->>API: { session: { user, expires }, authenticated: true }
    API-->>App: SessionResponse
    alt session.authenticated === true
        App->>Client: 200 { session: ... }
    else session.authenticated === false
        App->>Client: 401 { session: null, authenticated: false }
    end
Loading
sequenceDiagram
    participant Client as Client
    participant App as Application
    participant API as api.signOut()
    participant SignOutAction as SignOut Action
    participant Cookies as Cookie Store
    participant JWT as Jose JWT
    participant CSRF as CSRF Verification

    Client->>App: POST /api/signout
    App->>API: api.signOut({ headers, redirectTo, skipCSRFCheck })
    API->>SignOutAction: signOut({ ctx, headers, redirectTo, skipCSRFCheck })
    SignOutAction->>Cookies: getCookie(headers, sessionToken)
    SignOutAction->>Cookies: getCookie(headers, csrfToken)
    alt skipCSRFCheck === true
        SignOutAction->>CSRF: verifyJWS(csrfToken)
        CSRF-->>SignOutAction: verified
    else skipCSRFCheck === false
        SignOutAction->>SignOutAction: validate CSRF header
        SignOutAction->>JWT: decodeJWT(csrfToken)
        JWT-->>SignOutAction: decoded
    end
    SignOutAction->>JWT: decodeJWT(sessionToken)
    JWT-->>SignOutAction: session payload
    SignOutAction-->>API: Response (202, expired cookies, Location redirect)
    API-->>App: Response
    App->>Client: 202 Redirect
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • PR #90: Modifies authentication action headers by introducing/using secureApiHeaders with content security policy adjustments and action flow changes—related through session action header handling patterns.
  • PR #96: Updates Express example auth surface by exposing new api property on AuthInstance and refactoring session helper usage—directly related through identical auth export changes.
  • PR #101: Introduces centralized API/client factory surface (createAPI/createClient) and updates code to use new unified session/sign-out methods—core-level refactoring with identical API wiring pattern.

Poem

🐰 A hop through the auth, a new path appears,
The api now gathers what once lived dispersed.
From corners and edges, the session comes clean,
One true interface for auth ever seen.
No more scattered getSession in the wild—
The burrow's rebuilt, organized, mild! 🌱

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(core): introduce api object via createAuth' directly and clearly summarizes the main change: introducing an api object via createAuth. It is specific, concise, and accurately reflects the primary objective of the PR.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/introduce-server-api

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

❤️ Share

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

Copy link

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

🧹 Nitpick comments (5)
packages/core/src/index.ts (2)

67-78: Pin createAuth return type to AuthInstance.

Adding an explicit return type makes contract drift detectable at compile time.

♻️ Proposed typing update
-import type { AuthConfig } from "@/@types/index.ts"
+import type { AuthConfig, AuthInstance } from "@/@types/index.ts"
@@
-export const createAuth = (authConfig: AuthConfig) => {
+export const createAuth = (authConfig: AuthConfig): AuthInstance => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/index.ts` around lines 67 - 78, The createAuth function
currently returns an inferred type; explicitly annotate its return as
AuthInstance to prevent contract drift—update the createAuth signature to return
AuthInstance (while keeping the body using createInternalConfig and createRouter
with signInAction, callbackAction, sessionAction, signOutAction,
csrfTokenAction) so the compiler enforces the public shape (handlers, jose,
server) matches the AuthInstance interface.

8-24: Re-export the new server/session types from the public barrel.

SessionResponse and AuthServerAPI are introduced but not exposed here, which makes consumer typing less discoverable.

♻️ Proposed export update
 export type {
     AuthConfig,
     AuthInstance,
+    AuthServerAPI,
     JoseInstance,
     Session,
+    SessionResponse,
     User,
     CookieConfig,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/index.ts` around lines 8 - 24, The public barrel in
packages/core/src/index.ts currently re-exports many types but omits the new
server/session types; add SessionResponse and AuthServerAPI to the exported
types list so consumers can import them from the public package. Update the
export block that currently lists AuthConfig, AuthInstance, JoseInstance,
Session, User, etc., to include SessionResponse and AuthServerAPI (the unique
symbols to add) alongside the existing exports.
packages/core/src/context.ts (2)

33-33: Avoid parsing TRUSTED_ORIGINS twice.

Line 33 computes the same env array two times; caching it once makes this path clearer.

♻️ Proposed cleanup
+    const trustedOriginsEnv = getEnvArray("TRUSTED_ORIGINS")
@@
-        trustedOrigins: getEnvArray("TRUSTED_ORIGINS").length > 0 ? getEnvArray("TRUSTED_ORIGINS") : config?.trustedOrigins,
+        trustedOrigins: trustedOriginsEnv.length > 0 ? trustedOriginsEnv : config?.trustedOrigins,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/context.ts` at line 33, The trustedOrigins initialization
currently calls getEnvArray("TRUSTED_ORIGINS") twice; store the result in a
local variable (e.g., const envTrusted = getEnvArray("TRUSTED_ORIGINS")) and use
that single cached value in the ternary expression that sets trustedOrigins
(fallback to config?.trustedOrigins) to avoid duplicate parsing and clarify
intent in the trustedOrigins assignment.

10-13: Rename cookieCofig before this typo spreads further.

Line 10 and Line 35 use cookieCofig; renaming to cookieConfig now will avoid avoidable churn across the new server API wiring.

♻️ Proposed rename in this file
 export type InternalContext = GlobalContext & {
-    cookieCofig: {
+    cookieConfig: {
         secure: CookieStoreConfig
         standard: CookieStoreConfig
     }
 }
@@
-        cookieCofig: { secure: secureCookieStore, standard: standardCookieStore },
+        cookieConfig: { secure: secureCookieStore, standard: standardCookieStore },

Also applies to: 35-35

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

In `@packages/core/src/context.ts` around lines 10 - 13, Rename the misspelled
property cookieCofig to cookieConfig throughout the module: update the
type/interface where cookieCofig is declared and any references (e.g., in the
context object and usages that read/write cookieCofig) so the symbol is
consistently cookieConfig; ensure related types CookieStoreConfig remain
unchanged and run the type-checker to catch remaining references to cookieCofig
(update imports/exports if the property is exposed).
packages/core/src/actions/session/session.ts (1)

12-17: Prefer direct branching over throw/catch for expected unauthenticated flow

Line 13–Line 15 uses exceptions for a non-exceptional branch and then handles it locally. A direct unauthorized return path is simpler and removes unnecessary control-flow indirection.

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

In `@packages/core/src/actions/session/session.ts` around lines 12 - 17, Replace
the throw-based unauthenticated branch in the server.getSession handling with a
direct unauthorized return: after calling server.getSession(request) check
session.authenticated and if false return an unauthorized Response (e.g.,
Response.json({ error: "INVALID_JWT_TOKEN", message: "Session not authenticated"
}, { status: 401, headers: secureApiHeaders })) instead of throwing
AuthInternalError; leave the try/catch to handle real exceptions and remove the
local throw usage around AuthInternalError so control flow is direct and clearer
in the code that calls server.getSession and builds the Response.json.
🤖 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/deno/src/index.ts`:
- Line 1: The code uses server.getSession but never imports server; add an
import for the exported server symbol from the module that defines it (the same
module that exports the server instance used elsewhere) and include it alongside
the existing handlers import so calls to server.getSession resolve correctly;
update the import statement that currently reads import { handlers } from
"./auth.ts" to also import server (e.g., import { handlers, server } from
<module-that-exports-server>) so server.getSession(...) works.
- Around line 9-10: The authorization check incorrectly tests the session object
itself (const session = await server.getSession(request); if (!session)) which
is always truthy; update the conditional to check the discriminant returned by
getSession instead (use if (!session.authenticated)) so unauthenticated requests
are rejected; adjust the branch that handles unauthorized access to respond with
the same 401/unauthorized behavior used in other integrations and keep the
variable name session and call site server.getSession(request) unchanged.

In `@apps/oak/src/middleware/with-auth.ts`:
- Line 21: The with-auth middleware references server.getSession(ctx.request)
but `server` is not declared or imported; fix by making the auth server
available to this module—either import the server instance used elsewhere (e.g.,
from your auth/server or auth/index export) or accept it as a parameter to the
withAuth function and use that parameter instead; update the call site to pass
the server if you change the signature, and ensure the code uses the correct
symbol (e.g., server.getSession or injectedServer.getSession) so the getSession
call is in-scope.

In `@apps/supabase/functions/auth/index.ts`:
- Line 15: The current check uses session.user which can throw when getSession()
returns null; change the authorization guard to use the discriminant
session?.authenticated (from the getSession() result) instead of accessing
session.user, and update any downstream logic in the auth handler in
apps/supabase/functions/auth/index.ts to rely on session when authenticated is
true (or handle null session) so you no longer dereference session.user when
session may be null.

In `@packages/core/src/server/create-server.ts`:
- Around line 6-12: getSession currently always reads the session cookie from
ctx.cookies.sessionToken.name (in createServerAPI -> getSession), which breaks
request-dependent cookie selection; update getSession to pick the cookie store
based on the incoming Request (use the same secure/standard selection logic used
elsewhere in your codebase) and then read the session token from that
request-specific cookie store instead of ctx.cookies.sessionToken.name so
HTTP/local requests use the correct cookie namespace.

---

Nitpick comments:
In `@packages/core/src/actions/session/session.ts`:
- Around line 12-17: Replace the throw-based unauthenticated branch in the
server.getSession handling with a direct unauthorized return: after calling
server.getSession(request) check session.authenticated and if false return an
unauthorized Response (e.g., Response.json({ error: "INVALID_JWT_TOKEN",
message: "Session not authenticated" }, { status: 401, headers: secureApiHeaders
})) instead of throwing AuthInternalError; leave the try/catch to handle real
exceptions and remove the local throw usage around AuthInternalError so control
flow is direct and clearer in the code that calls server.getSession and builds
the Response.json.

In `@packages/core/src/context.ts`:
- Line 33: The trustedOrigins initialization currently calls
getEnvArray("TRUSTED_ORIGINS") twice; store the result in a local variable
(e.g., const envTrusted = getEnvArray("TRUSTED_ORIGINS")) and use that single
cached value in the ternary expression that sets trustedOrigins (fallback to
config?.trustedOrigins) to avoid duplicate parsing and clarify intent in the
trustedOrigins assignment.
- Around line 10-13: Rename the misspelled property cookieCofig to cookieConfig
throughout the module: update the type/interface where cookieCofig is declared
and any references (e.g., in the context object and usages that read/write
cookieCofig) so the symbol is consistently cookieConfig; ensure related types
CookieStoreConfig remain unchanged and run the type-checker to catch remaining
references to cookieCofig (update imports/exports if the property is exposed).

In `@packages/core/src/index.ts`:
- Around line 67-78: The createAuth function currently returns an inferred type;
explicitly annotate its return as AuthInstance to prevent contract drift—update
the createAuth signature to return AuthInstance (while keeping the body using
createInternalConfig and createRouter with signInAction, callbackAction,
sessionAction, signOutAction, csrfTokenAction) so the compiler enforces the
public shape (handlers, jose, server) matches the AuthInstance interface.
- Around line 8-24: The public barrel in packages/core/src/index.ts currently
re-exports many types but omits the new server/session types; add
SessionResponse and AuthServerAPI to the exported types list so consumers can
import them from the public package. Update the export block that currently
lists AuthConfig, AuthInstance, JoseInstance, Session, User, etc., to include
SessionResponse and AuthServerAPI (the unique symbols to add) alongside the
existing exports.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf0e175 and 8297c89.

📒 Files selected for processing (28)
  • apps/bun/src/auth.ts
  • apps/bun/src/index.ts
  • apps/bun/src/lib/get-session.ts
  • apps/deno/src/auth.ts
  • apps/deno/src/index.ts
  • apps/deno/src/lib/get-session.ts
  • apps/elysia/src/auth.ts
  • apps/elysia/src/lib/get-session.ts
  • apps/elysia/src/plugins/with-auth.ts
  • apps/express/src/auth.ts
  • apps/express/src/lib/get-session.ts
  • apps/express/src/lib/verify-session.ts
  • apps/hono/src/auth.ts
  • apps/hono/src/lib/get-session.ts
  • apps/hono/src/middleware/with-auth.ts
  • apps/oak/src/auth.ts
  • apps/oak/src/lib/get-session.ts
  • apps/oak/src/middleware/with-auth.ts
  • apps/supabase/functions/_shared/auth.ts
  • apps/supabase/functions/_shared/get-session.ts
  • apps/supabase/functions/auth/index.ts
  • packages/core/.vscode/settings.json
  • packages/core/src/@types/index.ts
  • packages/core/src/actions/session/session.ts
  • packages/core/src/context.ts
  • packages/core/src/index.ts
  • packages/core/src/server/create-server.ts
  • packages/core/test/actions/session/session.test.ts
💤 Files with no reviewable changes (7)
  • apps/bun/src/lib/get-session.ts
  • apps/deno/src/lib/get-session.ts
  • apps/supabase/functions/_shared/get-session.ts
  • apps/elysia/src/lib/get-session.ts
  • apps/oak/src/lib/get-session.ts
  • apps/express/src/lib/get-session.ts
  • apps/hono/src/lib/get-session.ts

@halvaradop halvaradop changed the title feat(core): introduce server API via createAuth feat(core): introduce api object via createAuth Mar 5, 2026
Fixes stale build artifacts that prevented AuthInstance API types from being recognized.
@halvaradop halvaradop added enhancement New feature or request feature New functionality labels Mar 6, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

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

⚠️ Outside diff range comments (2)
apps/elysia/src/plugins/with-auth.ts (1)

7-18: ⚠️ Potential issue | 🟠 Major

Unwrap SessionResponse before storing it in ctx.session.

api.getSession() returns { session, authenticated }, but the plugin currently returns that entire object under session. This makes ctx.session the wrapper instead of the actual Session, causing /api/auth/me to expose the wrapped shape.

Suggested fix
         try {
             const session = await api.getSession({
                 headers: ctx.request.headers,
             })
-            if (!session!.authenticated) {
+            if (!session.authenticated) {
                 return { session: null }
             }
-            return { session }
+            return { session: session.session }
         } catch {
             return { session: null }
         }
     })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/elysia/src/plugins/with-auth.ts` around lines 7 - 18, The plugin is
storing the entire SessionResponse wrapper in ctx.session; change the handler
that calls api.getSession() (the call to api.getSession and the surrounding
anonymous async function) to extract the inner Session object (e.g., const {
session: actualSession, authenticated } = await api.getSession(...)) and
set/return session: actualSession (or null if not authenticated or on error) so
ctx.session holds the raw Session; also ensure the get("/api/auth/me", ({
session }) => session) continues to return the unwrapped Session or null.
apps/nextjs/app-router/src/lib/server.ts (1)

59-71: ⚠️ Potential issue | 🟠 Major

api.signOut requires CSRF token in headers for security validation.

Based on the core signOut API (packages/core/src/api/signOut.ts), the function validates the X-CSRF-Token header unless skipCSRFCheck is set. The current implementation passes only the request headers without ensuring the CSRF token header is present, which will cause AuthSecurityError("CSRF_HEADER_MISSING") to be thrown.

🔧 Suggested approach

Either:

  1. Retrieve the CSRF token and include it in headers before calling api.signOut:
 export const signOut = async (redirectTo: string = "/") => {
     try {
         const cookieStore = await cookies()
+        const csrfToken = await getCSRFToken()
+        const headersInit = await headers()
+        const signOutHeaders = new Headers(headersInit)
+        if (csrfToken) {
+            signOutHeaders.set("X-CSRF-Token", csrfToken)
+        }
         const response = await api.signOut({
-            headers: await headers(),
+            headers: signOutHeaders,
         })
  1. Or expose a skipCSRFCheck option for server-side calls where the request is already authenticated.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/app-router/src/lib/server.ts` around lines 59 - 71, The call to
api.signOut currently forwards only headers() and may omit the required
X-CSRF-Token, causing AuthSecurityError; update the sign-out flow in this block
(cookies(), headers(), api.signOut) to include the CSRF token header or opt out
of validation: either extract the CSRF token from headers() (or from wherever
your app stores it) and add it as "X-CSRF-Token" to the headers passed into
api.signOut, or call api.signOut with the skipCSRFCheck option enabled (e.g., {
headers: await headers(), skipCSRFCheck: true }) so the server-side call doesn’t
require the header. Ensure the chosen fix is applied where api.signOut is
invoked and that cookieStore.delete/name parsing remains unchanged.
♻️ Duplicate comments (3)
apps/astro/src/auth.ts (1)

6-6: ⚠️ Potential issue | 🔴 Critical

Same AuthInstance / api mismatch as the other auth adapters.

This export has the same blocking type error: the AuthInstance resolved by this app does not currently expose api, so the adapter will not type-check until the package/dependency surface is aligned.

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

In `@apps/astro/src/auth.ts` at line 6, The export destructures { handlers, jose,
api } from createAuth but the resolved AuthInstance type does not include api;
update this by either adding api to the AuthInstance interface/type or by
removing api from the destructure/export so the exported symbols match the
actual AuthInstance shape — locate the createAuth call and the AuthInstance type
definition and either extend AuthInstance to include an api property (with the
correct type) or change the export to export only handlers and jose to ensure
the adapter type-checks.
apps/nextjs/pages-router/src/auth.ts (1)

6-6: ⚠️ Potential issue | 🔴 Critical

Same AuthInstance / api mismatch as the other auth adapters.

This export hits the same compile-time failure: the AuthInstance seen by CI does not currently expose api, so this file will not type-check until the package/dependency surface is aligned.

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

In `@apps/nextjs/pages-router/src/auth.ts` at line 6, The exported destructure
includes `api` which is not present on the `AuthInstance` type causing a type
mismatch; either remove `api` from the export or align the type to include it.
Locate the line exporting `export const { handlers, jose, api }: AuthInstance =
createAuth({` and fix by (A) dropping `api` from the destructured export if
`createAuth` does not return it, or (B) update/extend the `AuthInstance` type
definition to include the `api` symbol (or cast the result of `createAuth` to a
type that includes `api`) so the declared type matches the actual returned
shape.
apps/oak/src/auth.ts (1)

3-3: ⚠️ Potential issue | 🔴 Critical

Same AuthInstance / api mismatch as the other auth adapters.

CI reports the same failure here, so this file will not compile until the resolved @aura-stack/auth typings include api.

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

In `@apps/oak/src/auth.ts` at line 3, The export destructures handlers, jose and
api from createAuth but the annotated type AuthInstance (used in the declaration
"export const { handlers, jose, api }: AuthInstance") does not yet include api;
update the declaration so it compiles by either (a) updating the AuthInstance
type in the `@aura-stack/auth` typings to include the api property, or (b)
removing the explicit ": AuthInstance" annotation and let TypeScript infer the
returned shape from createAuth (keeping "export const { handlers, jose, api } =
createAuth(...)" ), referencing the createAuth call and the exported symbols
handlers, jose, api.
🧹 Nitpick comments (2)
apps/nextjs/app-router/src/app/page.tsx (1)

10-10: Minor: signOut is destructured but unused in this file.

Consider removing the unused signOut destructuring to keep the code clean, unless it's planned for use in an upcoming change.

✂️ Remove unused destructuring
-    const { getSession, signIn, signOut } = createAuthServer
+    const { getSession, signIn } = createAuthServer
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/nextjs/app-router/src/app/page.tsx` at line 10, The destructuring of
createAuthServer currently pulls getSession, signIn and signOut but signOut is
unused; update the destructuring expression that references createAuthServer to
remove signOut (keep getSession and signIn) to eliminate the unused variable and
related linter warning, ensuring no other references to signOut remain in the
file (e.g., search for signOut usage before removing).
packages/core/src/utils.ts (1)

173-177: Consider adding a clarifying comment for the security intent.

The function correctly restricts redirects to relative URLs only (preventing open redirect attacks to external domains). However, the logic flow may be unclear to future maintainers:

  1. Malformed URLs → "/"
  2. Relative URLs → allowed (internal redirects)
  3. Valid absolute URLs → "/" (blocked for security)
📝 Suggested documentation improvement
+/**
+ * Validates and sanitizes redirect URLs to prevent open redirect attacks.
+ * Only relative URLs (starting with /) are allowed; absolute URLs are
+ * rejected and replaced with "/" to enforce same-origin redirects.
+ */
 export const validateRedirectTo = (url: string): string => {
     if (!isRelativeURL(url) && !isValidURL(url)) return "/"
     if (isRelativeURL(url)) return url
     return "/"
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/utils.ts` around lines 173 - 177, The validateRedirectTo
function's logic is correct but needs an inline clarifying comment about the
security intent: add a short comment above validateRedirectTo explaining that
only relative URLs are allowed to prevent open-redirect attacks, that malformed
or absolute URLs are intentionally normalized to "/" (internal root), and the
ordering (malformed → "/", relative → allowed, absolute → "/") is deliberate;
reference the function name validateRedirectTo and mention isRelativeURL and
isValidURL so maintainers understand the rationale.
🤖 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/astro/src/lib/server.ts`:
- Around line 50-55: The sign-out flow discards session-clearing Set-Cookie
headers by calling redirect(redirectTo) after api.signOut(), so instead of
calling redirect inside the if (response.status === 202) branch, return the
Response returned from api.signOut() directly (i.e. return response) so the
Set-Cookie headers from api.signOut() are preserved and sent to the client; keep
using redirect(redirectTo) only for other non-202 cases if needed.

In `@apps/nextjs/app-router/src/lib/client.ts`:
- Around line 36-37: The signIn function currently redirects to a hard-coded
/auth/signIn/... which 404s; update signIn to use the same auth base path used
by the server-side helper in server.ts (the auth path builder at lines ~53-56)
rather than the hard-coded "/auth" so the client and server routes match. Locate
signIn and replace the hard-coded path with the shared auth base (or import and
reuse the server helper / AUTH_BASE_PATH constant) to build
`${AUTH_BASE_PATH}/signIn/${provider}?redirectTo=${encodeURIComponent(redirectTo)}`
so OAuth starts correctly.
- Around line 25-29: The getSession helper is still expecting a top-level user
on the JSON response but the API now returns { authenticated, session }; update
the parsing logic after client.get("/session") to read the JSON into a payload,
unwrap the actual session from payload.session (falling back to the old shape if
needed), and use payload.authenticated (or payload.session presence) when
deciding to return the session; adjust the console.log to log the unwrapped
session instead of the full payload. Target the getSession function around the
client.get("/session") call and replace the current response.json()/session/user
checks with the described unwrap-and-check logic.

In `@apps/nextjs/pages-router/src/lib/server.ts`:
- Around line 9-22: The module currently exports only the standalone getSession
function but consuming code expects createAuthServer.getSession; fix this by
exporting createAuthServer as an object that exposes getSession (e.g., add an
export like export const createAuthServer = { getSession }) so callers can call
createAuthServer.getSession(req); ensure the exported object signature matches
the existing getSession( req: NextApiRequest ): Promise<Session | null> and
leave the existing getSession export intact or re-export it from
createAuthServer to avoid breaking imports.

In `@packages/core/src/api/createApi.ts`:
- Around line 18-20: The public wrapper signOut in createApi.ts should not
force-disable CSRF; remove the hardcoded skipCSRFCheck: true (or forward an
explicit options.skipCSRFCheck that defaults to false) when calling the internal
signOut function so header-bound CSRF validation remains enabled for public
callers, leaving bypass only for internal helpers in signOut.ts.

In `@packages/core/src/api/signOut.ts`:
- Around line 61-63: The else branch calls await ctx.jose.verifyJWS(csrfToken)
without handling rejections; wrap that call in a try/catch and convert any
verification failure into the same AuthSecurityError used on the non-skip path
(throw new AuthSecurityError or reuse the existing helper) so malformed/expired
CSRF tokens produce the auth error contract; ensure you reference
ctx.jose.verifyJWS and AuthSecurityError (and import it if necessary) and mirror
the error message/handling used by the non-skip branch in signOut.
- Around line 70-80: The response currently sets a Location header via
HeadersBuilder.setHeader("Location", redirectTo) but returns status 202, which
browsers ignore for redirects; update the signOut response so that when a
redirectTo is present (or always, if intended) the Response.json call uses a 303
status (See Other) instead of 202 — or conditionally return 303 when redirectTo
is provided and 202 otherwise; modify the code around HeadersBuilder/headersList
and the Response.json(...) status value accordingly.

---

Outside diff comments:
In `@apps/elysia/src/plugins/with-auth.ts`:
- Around line 7-18: The plugin is storing the entire SessionResponse wrapper in
ctx.session; change the handler that calls api.getSession() (the call to
api.getSession and the surrounding anonymous async function) to extract the
inner Session object (e.g., const { session: actualSession, authenticated } =
await api.getSession(...)) and set/return session: actualSession (or null if not
authenticated or on error) so ctx.session holds the raw Session; also ensure the
get("/api/auth/me", ({ session }) => session) continues to return the unwrapped
Session or null.

In `@apps/nextjs/app-router/src/lib/server.ts`:
- Around line 59-71: The call to api.signOut currently forwards only headers()
and may omit the required X-CSRF-Token, causing AuthSecurityError; update the
sign-out flow in this block (cookies(), headers(), api.signOut) to include the
CSRF token header or opt out of validation: either extract the CSRF token from
headers() (or from wherever your app stores it) and add it as "X-CSRF-Token" to
the headers passed into api.signOut, or call api.signOut with the skipCSRFCheck
option enabled (e.g., { headers: await headers(), skipCSRFCheck: true }) so the
server-side call doesn’t require the header. Ensure the chosen fix is applied
where api.signOut is invoked and that cookieStore.delete/name parsing remains
unchanged.

---

Duplicate comments:
In `@apps/astro/src/auth.ts`:
- Line 6: The export destructures { handlers, jose, api } from createAuth but
the resolved AuthInstance type does not include api; update this by either
adding api to the AuthInstance interface/type or by removing api from the
destructure/export so the exported symbols match the actual AuthInstance shape —
locate the createAuth call and the AuthInstance type definition and either
extend AuthInstance to include an api property (with the correct type) or change
the export to export only handlers and jose to ensure the adapter type-checks.

In `@apps/nextjs/pages-router/src/auth.ts`:
- Line 6: The exported destructure includes `api` which is not present on the
`AuthInstance` type causing a type mismatch; either remove `api` from the export
or align the type to include it. Locate the line exporting `export const {
handlers, jose, api }: AuthInstance = createAuth({` and fix by (A) dropping
`api` from the destructured export if `createAuth` does not return it, or (B)
update/extend the `AuthInstance` type definition to include the `api` symbol (or
cast the result of `createAuth` to a type that includes `api`) so the declared
type matches the actual returned shape.

In `@apps/oak/src/auth.ts`:
- Line 3: The export destructures handlers, jose and api from createAuth but the
annotated type AuthInstance (used in the declaration "export const { handlers,
jose, api }: AuthInstance") does not yet include api; update the declaration so
it compiles by either (a) updating the AuthInstance type in the `@aura-stack/auth`
typings to include the api property, or (b) removing the explicit ":
AuthInstance" annotation and let TypeScript infer the returned shape from
createAuth (keeping "export const { handlers, jose, api } = createAuth(...)" ),
referencing the createAuth call and the exported symbols handlers, jose, api.

---

Nitpick comments:
In `@apps/nextjs/app-router/src/app/page.tsx`:
- Line 10: The destructuring of createAuthServer currently pulls getSession,
signIn and signOut but signOut is unused; update the destructuring expression
that references createAuthServer to remove signOut (keep getSession and signIn)
to eliminate the unused variable and related linter warning, ensuring no other
references to signOut remain in the file (e.g., search for signOut usage before
removing).

In `@packages/core/src/utils.ts`:
- Around line 173-177: The validateRedirectTo function's logic is correct but
needs an inline clarifying comment about the security intent: add a short
comment above validateRedirectTo explaining that only relative URLs are allowed
to prevent open-redirect attacks, that malformed or absolute URLs are
intentionally normalized to "/" (internal root), and the ordering (malformed →
"/", relative → allowed, absolute → "/") is deliberate; reference the function
name validateRedirectTo and mention isRelativeURL and isValidURL so maintainers
understand the rationale.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f9363638-60da-498b-a190-50e868f28250

📥 Commits

Reviewing files that changed from the base of the PR and between 8297c89 and 8e66e0e.

📒 Files selected for processing (40)
  • apps/astro/src/auth.ts
  • apps/astro/src/lib/server.ts
  • apps/bun/src/auth.ts
  • apps/bun/src/index.ts
  • apps/cloudflare/src/auth.ts
  • apps/deno/src/auth.ts
  • apps/deno/src/index.ts
  • apps/elysia/src/auth.ts
  • apps/elysia/src/plugins/with-auth.ts
  • apps/express/src/auth.ts
  • apps/express/src/lib/verify-session.ts
  • apps/express/test/index.test.ts
  • apps/hono/src/auth.ts
  • apps/hono/src/middleware/with-auth.ts
  • apps/nextjs/app-router/src/app/page.tsx
  • apps/nextjs/app-router/src/app/profile/page.tsx
  • apps/nextjs/app-router/src/auth.ts
  • apps/nextjs/app-router/src/lib/client.ts
  • apps/nextjs/app-router/src/lib/server.ts
  • apps/nextjs/pages-router/src/auth.ts
  • apps/nextjs/pages-router/src/lib/server.ts
  • apps/oak/src/auth.ts
  • apps/oak/src/middleware/with-auth.ts
  • apps/react-router/app/actions/auth.server.ts
  • apps/react-router/app/auth.ts
  • apps/supabase/functions/_shared/auth.ts
  • apps/supabase/functions/auth/index.ts
  • apps/tanstack-start/src/auth.ts
  • packages/core/CHANGELOG.md
  • packages/core/src/@types/index.ts
  • packages/core/src/actions/session/session.ts
  • packages/core/src/actions/signOut/signOut.ts
  • packages/core/src/api/createApi.ts
  • packages/core/src/api/getSession.ts
  • packages/core/src/api/signOut.ts
  • packages/core/src/context.ts
  • packages/core/src/cookie.ts
  • packages/core/src/index.ts
  • packages/core/src/utils.ts
  • packages/core/test/cookie.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/bun/src/auth.ts
  • apps/hono/src/auth.ts
  • apps/express/src/auth.ts
  • apps/deno/src/index.ts
  • apps/supabase/functions/_shared/auth.ts
  • apps/deno/src/auth.ts

@vercel vercel bot temporarily deployed to Preview – auth March 6, 2026 01:08 Inactive
@halvaradop halvaradop merged commit 200be9e into master Mar 6, 2026
6 of 8 checks passed
@halvaradop halvaradop deleted the feat/introduce-server-api branch March 6, 2026 01:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request feature New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant