Skip to content

feat: add session transfer token support to CTE#2734

Merged
Piyush-85 merged 12 commits into
mainfrom
feat/cte-session-transfer-token
Jul 22, 2026
Merged

feat: add session transfer token support to CTE#2734
Piyush-85 merged 12 commits into
mainfrom
feat/cte-session-transfer-token

Conversation

@Piyush-85

@Piyush-85 Piyush-85 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor
  • All new/changed/fixed functionality is covered by tests (or N/A)

  • I have added documentation for all new/changed functionality (or N/A)

    📋 Changes

Implements CTE Phase 2 — Session Transfer Token (STT). An authenticated agent can now request a short-lived, one-shot token that lets them establish a web session as a customer in a target app, without the customer's password.

New methods on Auth0Client:

  • requestSessionTransferToken(options) — exchanges a subject token for an STT via POST /oauth/token with
    audience=urn:{domain}:session_transfer. The actor defaults to the agent session's ID token (checked for expiry); an
    explicit actor override is also supported.
  • buildSessionTransferRedirect(targetLoginUrl, result, opts?) — pure URL builder returning a NextResponse.redirect with ?session_transfer_token=<stt> appended.

New types exported from @auth0/nextjs-auth0/server and @auth0/nextjs-auth0/types:

  • SessionTransferTokenOptions / SessionTransferTokenResult
  • TOKEN_TYPES enum (ID_TOKEN, ACCESS_TOKEN, SESSION_TRANSFER_TOKEN)

New error codes on CustomTokenExchangeErrorCode:

  • ACTOR_UNAVAILABLE — no usable actor token (client-side, before network call)
  • SETACTOR_REQUIRED — server requires an actor to be set via api.authentication.setActor()
  • SESSION_TRANSFER_DISABLED — tenant feature flag not enabled

Implementation notes:

  • oauth4webapi's processGenericTokenEndpointResponse rejects token_type: "N_A" (returned by the STT endpoint). The implementation bypasses it by calling genericTokenEndpointRequest directly and parsing the raw JSON body.
  • STT-specific pure functions are extracted to src/utils/session-transfer-helpers.ts to keep auth-client.ts manageable.
  • No middleware change needed — handleLogin() already forwards all query params to /authorize, so
    session_transfer_token passes through transparently on the target app side.

📎 References

  • CTE Phase 2 / Session Transfer Token spec (internal Confluence)

🎯 Testing

  • 36 new unit tests in src/server/custom-token-exchange.test.ts (section 5) covering result shape, actor resolution, wire
    parameters, STT not persisted to session, server error mapping, input validation, buildSessionTransferRedirect, and
    backward compatibility.
  • 23 unit tests in src/utils/session-transfer-helpers.test.ts covering all 4 helper functions.
  • All 1739 tests pass, TypeScript clean, zero new console warnings from the STT code paths.

Summary by CodeRabbit

  • New Features
    • Added Session Transfer Token (STT) support with new request and redirect helpers for one-shot session transfer.
    • Exposed STT option/result types, token-type constants, and STT-related OAuth error codes.
  • Bug Fixes
    • Improved STT actor validation/resolution and safer handling of STT-specific token responses, including typed server error mapping.
  • Documentation
    • Added STT documentation to the Custom Token Exchange guide, including flow, error behavior, and limitations.
  • Tests
    • Added unit and end-to-end coverage for STT request/response contracts, redirect URL encoding, and query forwarding.

@Piyush-85
Piyush-85 requested a review from a team as a code owner July 6, 2026 06:32
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds Session Transfer Token support for RFC 8693 Custom Token Exchange Phase 2, including public types, typed errors, helper utilities, server client methods, redirects, tests, exports, and documentation.

Changes

Session Transfer Token feature

Layer / File(s) Summary
STT contracts, errors, and exports
src/types/token-vault.ts, src/errors/oauth-errors.ts, src/types/index.ts, src/server/index.ts
Adds STT token types, request/response interfaces, error codes, and public re-exports.
Session transfer helper utilities
src/utils/session-transfer-helpers.ts, src/utils/session-transfer-helpers.test.ts
Builds audiences and redirects, resolves actors, parses responses, and maps server errors with unit coverage.
AuthClient STT exchange and redirect
src/server/auth-client.ts
Implements the STT exchange, DPoP nonce retry, typed error handling, response parsing, and redirect construction.
Auth0Client public STT API
src/server/client.ts, src/server/client.test.ts
Exposes STT request and redirect methods using request headers and the current session.
STT validation and documentation
src/server/custom-token-exchange.test.ts, EXAMPLES.md
Adds coverage for exchange behavior, actor handling, parameters, errors, redirects, compatibility, login forwarding, and documented usage.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Auth0Client
  participant AuthClient
  participant SessionHelpers
  participant AuthorizationServer

  Caller->>Auth0Client: requestSessionTransferToken(options)
  Auth0Client->>Auth0Client: getSession()
  Auth0Client->>AuthClient: requestSessionTransferToken(options, session)
  AuthClient->>SessionHelpers: resolveActorFromSession(session, actor)
  SessionHelpers-->>AuthClient: actor token or error
  AuthClient->>AuthorizationServer: STT token exchange
  AuthorizationServer-->>AuthClient: raw response or DPoP nonce error
  AuthClient->>AuthorizationServer: retry with DPoP nonce
  AuthorizationServer-->>AuthClient: raw STT response
  AuthClient->>SessionHelpers: parseSessionTransferTokenResponse(raw)
  SessionHelpers-->>AuthClient: SessionTransferTokenResult
  AuthClient-->>Auth0Client: result or typed error
  Auth0Client-->>Caller: SessionTransferTokenResult
Loading

Possibly related PRs

  • auth0/nextjs-auth0#2690: Updates the shared actor and custom-token-exchange error model, including actor-token validation and act claim handling.

Suggested reviewers: tusharpandey13

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding Session Transfer Token support to CTE.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cte-session-transfer-token

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.

@codecov-commenter

codecov-commenter commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.71429% with 85 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.73%. Comparing base (019d1f8) to head (4fb34ba).

Files with missing lines Patch % Lines
src/server/auth-client.ts 66.66% 66 Missing ⚠️
src/utils/session-transfer-helpers.ts 89.62% 9 Missing and 2 partials ⚠️
src/server/client.ts 83.33% 6 Missing ⚠️
src/server/index.ts 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2734      +/-   ##
==========================================
- Coverage   88.11%   87.73%   -0.38%     
==========================================
  Files          79       80       +1     
  Lines       11105    11454     +349     
  Branches     2296     2366      +70     
==========================================
+ Hits         9785    10049     +264     
- Misses       1276     1359      +83     
- Partials       44       46       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/types/token-vault.ts (1)

235-244: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider narrowing issuedTokenType's type.

The doc comment instructs callers to "Branch on issuedTokenType", but it's typed as plain string, losing type-narrowing benefits for the primary documented usage pattern.

♻️ Proposed refactor
-  issuedTokenType: string;
+  issuedTokenType: TOKEN_TYPES.SESSION_TRANSFER_TOKEN | string;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/types/token-vault.ts` around lines 235 - 244, The
SessionTransferTokenResult interface documents issuedTokenType as the field
callers should branch on, but it is currently typed too broadly as string.
Update the SessionTransferTokenResult type to narrow issuedTokenType to the
specific session transfer token discriminator used in this module (for example,
the corresponding TOKEN_TYPES.SESSION_TRANSFER_TOKEN literal type), so consumers
of buildSessionTransferRedirect and related token-vault helpers get proper type
narrowing when checking it.
src/utils/session-transfer-helpers.ts (1)

99-111: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider naming the default expiresIn fallback.

The literal 60 on Line 108 is a magic number with no inline explanation of why 60 seconds is the fallback. A named constant (e.g., DEFAULT_STT_EXPIRES_IN_SECONDS) would make the intent explicit at the call site.

♻️ Proposed refactor
+const DEFAULT_STT_EXPIRES_IN_SECONDS = 60;
+
 export function parseSessionTransferTokenResponse(raw: {
   access_token: string;
   issued_token_type: string;
   expires_in?: number;
   token_type?: string;
 }): SessionTransferTokenResult {
   return {
     sessionTransferToken: raw.access_token,
     issuedTokenType: raw.issued_token_type,
-    expiresIn: Number(raw.expires_in ?? 60),
+    expiresIn: Number(raw.expires_in ?? DEFAULT_STT_EXPIRES_IN_SECONDS),
     tokenType: raw.token_type
   };
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/session-transfer-helpers.ts` around lines 99 - 111, The
`parseSessionTransferTokenResponse` helper currently uses a magic number for the
`expiresIn` fallback, so replace the inline `60` with a clearly named constant
near the function (for example a default session transfer token expiry value)
and use that constant in the returned `SessionTransferTokenResult` to make the
intent explicit.
src/utils/session-transfer-helpers.test.ts (1)

8-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Import TOKEN_TYPES from the same barrel as SessionData.

SessionData is imported from ../types/index.js while TOKEN_TYPES is imported directly from ../types/token-vault.js, even though TOKEN_TYPES is now re-exported from ../types/index.js too. Consolidating to one import source avoids coupling the test to the internal file layout.

♻️ Proposed refactor
-import { SessionData } from "../types/index.js";
-import { TOKEN_TYPES } from "../types/token-vault.js";
+import { SessionData, TOKEN_TYPES } from "../types/index.js";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/session-transfer-helpers.test.ts` around lines 8 - 9, Update the
imports in the session transfer helper test so both SessionData and TOKEN_TYPES
come from the same barrel export in ../types/index.js. The issue is that
TOKEN_TYPES is still imported directly from ../types/token-vault.js even though
it is re-exported, so adjust the test’s import statements to use the shared
index barrel and remove the direct file import to keep the test decoupled from
internal file layout.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@EXAMPLES.md`:
- Around line 177-182: The table of contents entry for the repeated “Error
Handling” heading is using the wrong anchor, so update the TOC item in
EXAMPLES.md to match the sixth occurrence count used by the markdown renderer.
Keep the surrounding TOC entries aligned with the duplicate-heading pattern
already used for “Basic Usage”, “With Organization”, and “Limitations”, and
change the `Error Handling` anchor from the current value to the correct one for
the section.

---

Nitpick comments:
In `@src/types/token-vault.ts`:
- Around line 235-244: The SessionTransferTokenResult interface documents
issuedTokenType as the field callers should branch on, but it is currently typed
too broadly as string. Update the SessionTransferTokenResult type to narrow
issuedTokenType to the specific session transfer token discriminator used in
this module (for example, the corresponding TOKEN_TYPES.SESSION_TRANSFER_TOKEN
literal type), so consumers of buildSessionTransferRedirect and related
token-vault helpers get proper type narrowing when checking it.

In `@src/utils/session-transfer-helpers.test.ts`:
- Around line 8-9: Update the imports in the session transfer helper test so
both SessionData and TOKEN_TYPES come from the same barrel export in
../types/index.js. The issue is that TOKEN_TYPES is still imported directly from
../types/token-vault.js even though it is re-exported, so adjust the test’s
import statements to use the shared index barrel and remove the direct file
import to keep the test decoupled from internal file layout.

In `@src/utils/session-transfer-helpers.ts`:
- Around line 99-111: The `parseSessionTransferTokenResponse` helper currently
uses a magic number for the `expiresIn` fallback, so replace the inline `60`
with a clearly named constant near the function (for example a default session
transfer token expiry value) and use that constant in the returned
`SessionTransferTokenResult` to make the intent explicit.
🪄 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 Plus

Run ID: 725bbe96-437e-4fbe-bd79-9777bb03da07

📥 Commits

Reviewing files that changed from the base of the PR and between 9041e19 and ab1f430.

📒 Files selected for processing (10)
  • EXAMPLES.md
  • src/errors/oauth-errors.ts
  • src/server/auth-client.ts
  • src/server/client.ts
  • src/server/custom-token-exchange.test.ts
  • src/server/index.ts
  • src/types/index.ts
  • src/types/token-vault.ts
  • src/utils/session-transfer-helpers.test.ts
  • src/utils/session-transfer-helpers.ts

Comment thread EXAMPLES.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/utils/session-transfer-helpers.ts (2)

162-170: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Exact-match checks are redundant given the substring includes calls.

code === "setactor_required" is fully covered by code.includes("setactor"), and code === "session_transfer_disabled" is fully covered by code.includes("session_transfer"). The exact matches appear to serve as inline documentation of the expected platform codes, which is reasonable for readability — but if the intent is purely matching, the exact checks can be removed to reduce cognitive noise.

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

In `@src/utils/session-transfer-helpers.ts` around lines 162 - 170, Remove the
redundant exact equality checks from the conditionals mapping codes to
SETACTOR_REQUIRED and SESSION_TRANSFER_DISABLED, leaving the corresponding
includes checks and return behavior unchanged.

104-118: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add HTTP/HTTPS protocol validation to buildSessionTransferRedirectUrl.

new URL(targetLoginUrl) accepts any absolute URL including javascript:, data:, and file: schemes. While the doc comment states the URL must be "trusted, app-controlled," adding a protocol allowlist (http:/https:) is cheap defense-in-depth against open-redirect and XSS if the URL ever flows from partially untrusted input. The downstream caller in auth-client.ts uses the return value directly as a redirect target, so a non-HTTP scheme would propagate to the browser.

🛡️ Proposed defense-in-depth protocol check
   try {
     url = new URL(targetLoginUrl);
   } catch {
     throw new CustomTokenExchangeError(
       CustomTokenExchangeErrorCode.EXCHANGE_FAILED,
       `Invalid targetLoginUrl: "${targetLoginUrl}" is not an absolute URL. ` +
         "Pass a trusted, app-controlled absolute login URL (e.g. https://app.example.com/auth/login)."
     );
   }
+  if (url.protocol !== "http:" && url.protocol !== "https:") {
+    throw new CustomTokenExchangeError(
+      CustomTokenExchangeErrorCode.EXCHANGE_FAILED,
+      `Invalid targetLoginUrl: "${targetLoginUrl}" must use http or https.`
+    );
+  }
   url.searchParams.set("session_transfer_token", sessionTransferToken);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/session-transfer-helpers.ts` around lines 104 - 118, Update
buildSessionTransferRedirectUrl to validate the parsed URL’s protocol after new
URL(targetLoginUrl) succeeds, allowing only http: and https:. For any other
scheme, throw the same CustomTokenExchangeError used for invalid targetLoginUrl
input and include the URL value and requirement for an absolute HTTP/HTTPS login
URL in the message.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/utils/session-transfer-helpers.ts`:
- Around line 162-170: Remove the redundant exact equality checks from the
conditionals mapping codes to SETACTOR_REQUIRED and SESSION_TRANSFER_DISABLED,
leaving the corresponding includes checks and return behavior unchanged.
- Around line 104-118: Update buildSessionTransferRedirectUrl to validate the
parsed URL’s protocol after new URL(targetLoginUrl) succeeds, allowing only
http: and https:. For any other scheme, throw the same CustomTokenExchangeError
used for invalid targetLoginUrl input and include the URL value and requirement
for an absolute HTTP/HTTPS login URL in the message.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3bd8445c-be1b-43e1-9f7a-d7db43a40468

📥 Commits

Reviewing files that changed from the base of the PR and between ab1f430 and 0c6dc14.

📒 Files selected for processing (6)
  • EXAMPLES.md
  • src/server/auth-client.ts
  • src/server/client.ts
  • src/server/custom-token-exchange.test.ts
  • src/utils/session-transfer-helpers.test.ts
  • src/utils/session-transfer-helpers.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/server/client.ts
  • src/utils/session-transfer-helpers.test.ts
  • EXAMPLES.md
  • src/server/custom-token-exchange.test.ts
  • src/server/auth-client.ts

@Piyush-85
Piyush-85 force-pushed the feat/cte-session-transfer-token branch from 65af9ff to 4e50dcd Compare July 21, 2026 05:33
Comment thread src/utils/session-transfer-helpers.ts
Comment thread src/server/client.ts
Comment thread src/server/custom-token-exchange.test.ts
Comment thread EXAMPLES.md
Comment thread src/types/token-vault.ts Outdated
Comment thread EXAMPLES.md Outdated
Comment thread src/server/auth-client.ts Outdated
Comment thread src/utils/session-transfer-helpers.ts
Comment thread src/utils/session-transfer-helpers.ts Outdated
Comment thread src/server/auth-client.ts
@Piyush-85
Piyush-85 requested a review from kishore7snehil July 21, 2026 08:34
Comment thread EXAMPLES.md Outdated
Comment thread EXAMPLES.md
@Piyush-85
Piyush-85 requested a review from kishore7snehil July 21, 2026 09:32
kishore7snehil
kishore7snehil previously approved these changes Jul 22, 2026

@kishore7snehil kishore7snehil left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM!

Comment thread src/server/auth-client.ts Outdated
Comment thread src/server/custom-token-exchange.test.ts
Comment thread src/utils/session-transfer-helpers.ts
Comment thread src/server/auth-client.ts Outdated
@Piyush-85

Copy link
Copy Markdown
Contributor Author

@kishore7snehil @nandan-bhat Did another pass over the STT surface myself and found and fixed a few more things:

  • Refresh-on-actor-error was firing even for a bad explicit actor (not just a stale session token) — could waste a refresh call or throw a confusing MfaRequiredError for an unrelated input mistake. Now gated to only apply when no explicit actor was passed.
  • additionalParameters could duplicate organization/reason in the request body since neither was in the reserved-params denylist. Added both.
  • buildSessionTransferRedirectUrl's https-only/localhost scheme guard had zero test coverage. Added direct unit tests for it.
  • requestSessionTransferToken had no Pages Router overload, unlike every other method on Auth0Client. Added one, following the same pattern getAccessToken uses.
  • Added a defense-in-depth MCD domain check inside AuthClient.requestSessionTransferToken itself, so it's safe even if called directly without going through Auth0Client.getSession()'s domain check.

18 new tests added, full suite passing, TypeScript clean.

@Piyush-85
Piyush-85 merged commit 024720e into main Jul 22, 2026
9 checks passed
@Piyush-85
Piyush-85 deleted the feat/cte-session-transfer-token branch July 22, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants