Skip to content

Adding CTE session transfer token example#2747

Open
Piyush-85 wants to merge 10 commits into
mainfrom
feat/cte-stt-example
Open

Adding CTE session transfer token example#2747
Piyush-85 wants to merge 10 commits into
mainfrom
feat/cte-stt-example

Conversation

@Piyush-85

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

Copy link
Copy Markdown
Contributor
  • All new/changed/fixed functionality is covered by tests
  • I have added documentation for all new/changed functionality

📋 Changes

Adds a Session Transfer Token (STT) demo to the examples/with-cte example — the initiator flow where an authenticated agent establishes a web session as a customer in a target app, without the customer's password.

Depends on the SDK implementation PR (feat/cte-session-transfer-token). This branch is based on it.

What this example validates

  • auth0.requestSessionTransferToken() mints a one-shot STT, sourcing the actor from the agent's session ID token.
  • auth0.buildSessionTransferRedirect() builds the redirect that carries the STT to the target app's login URL.
  • The target app redeems the STT with no login/MFA/consent prompt and establishes an ephemeral session as the customer, with the agent recorded in the act claim (sub = customer, act.sub = agent, no refresh token).
  • The two-client setup and prerequisites (feature flag, delegated-access config, non-localhost callback) work end-to-end.

How to run

See the example README for setup and step-by-step instructions:
examples/with-cte/README.md

📎 References

  • SDK implementation PR: feat/cte-session-transfer-token
  • CTE Impersonation via Session Transfer Token (STT) — Auth SDK Requirements (Confluence, SDKREQ-379)
  • Session Transfer Token (STT) Testing (Confluence)

🎯 Testing

Manual, end-to-end against a live tenant following examples/with-cte/README.md. Verified: silent STT redemption, session established as the impersonated customer with act.sub = agent, and no refresh token issued.

Summary by CodeRabbit

  • New Features

    • Added a Session Transfer Token (STT) demo flow, including a new dashboard entry point and an STT page for requesting and transferring one-shot, short-lived sessions.
    • Implemented a server-side STT exchange endpoint with robust request validation and structured error responses.
    • Added a transfer form with optional advanced fields, redirect URL display, clipboard “Copy URL” action, and improved session-card refresh-token visibility (including impersonation behavior).
  • Documentation

    • Expanded the Custom Token Exchange example to cover STT enablement, constraints, running locally instructions, and error handling.
  • Chores

    • Added example-specific ignore rules via a new .gitignore.

@Piyush-85
Piyush-85 requested a review from a team as a code owner July 13, 2026 10:34
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 78f0b1be-c84b-4cf8-9d84-ea3838c99492

📥 Commits

Reviewing files that changed from the base of the PR and between bce6f76 and d1a41bf.

📒 Files selected for processing (1)
  • examples/with-cte/app/api/stt/route.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/with-cte/app/api/stt/route.ts

📝 Walkthrough

Walkthrough

The with-cte example now demonstrates Session Transfer Token requests and redemption through a protected API route, authenticated page, client form, dashboard navigation, and expanded setup and usage documentation.

Changes

Session Transfer Token example

Layer / File(s) Summary
STT exchange API
examples/with-cte/app/api/stt/route.ts
Adds authenticated request validation, target-origin checks, STT exchange and redirect creation, success responses, and structured error mapping.
STT demo UI and guidance
examples/with-cte/app/stt/*, examples/with-cte/app/dashboard/page.tsx, examples/with-cte/README.md, examples/with-cte/.gitignore
Adds the protected STT page and form, dashboard indicators and navigation, STT setup and walkthrough documentation, and local ignore rules.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AgentBrowser
  participant STTRoute
  participant Auth0Client
  participant TargetApp
  AgentBrowser->>STTRoute: Submit subject token and target login URL
  STTRoute->>Auth0Client: Request Session Transfer Token
  Auth0Client-->>STTRoute: Return redirect URL and expiration
  STTRoute-->>AgentBrowser: Display redirect URL
  AgentBrowser->>TargetApp: Open redirect URL
  TargetApp->>Auth0Client: Redeem session_transfer_token during login
Loading

Possibly related PRs

Suggested reviewers: kishore7snehil

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% 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 describes the main change: adding a CTE session transfer token example.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cte-stt-example

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.

@Piyush-85
Piyush-85 changed the base branch from main to feat/cte-session-transfer-token July 13, 2026 10:34

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

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

117-133: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Substring matching on server error codes may over-classify unrelated errors.

errorCode.toLowerCase().includes("setactor") / .includes("session_transfer") will reclassify any future/unexpected error code that happens to contain those substrings (e.g. a generic error mentioning "session_transfer" in a description-derived code) as SETACTOR_REQUIRED/SESSION_TRANSFER_DISABLED, masking the real EXCHANGE_FAILED case callers would otherwise get. Worth confirming this matches the actual set of Auth0 CTE server error codes, or tightening to an allow-list of exact known codes.

Can you confirm the exact set of server-side error codes returned for the setActor-required and session_transfer disabled cases (from the Auth0 CTE Action / tenant configuration), to validate whether substring matching is actually necessary here?

🤖 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 117 - 133, Tighten
mapSttServerError to classify only the exact, documented Auth0 CTE server error
codes for setActor-required and session-transfer-disabled cases. Remove broad
substring matching, use an explicit allow-list if multiple known variants exist,
and return null for all unrelated or unknown error codes.
src/types/token-vault.ts (1)

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

Consider narrowing issuedTokenType's type to match its documented contract.

The doc comment states issuedTokenType is "Always TOKEN_TYPES.SESSION_TRANSFER_TOKEN. Branch on this, not tokenType," but the field is typed as plain string, so TypeScript won't catch typos or incorrect comparisons when consumers branch on it.

♻️ Suggested tightening
-  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, Narrow
SessionTransferTokenResult.issuedTokenType from string to the specific
TOKEN_TYPES.SESSION_TRANSFER_TOKEN type (or its corresponding literal type),
preserving the documented invariant so consumers receive compile-time protection
when branching on this field.
src/server/custom-token-exchange.test.ts (1)

1225-1391: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add STT-specific DPoP nonce retry coverage.

STT bypasses the standard token-response processor and uses a bespoke retry path. Test a first use_dpop_nonce response followed by a successful nonce-bearing request to prevent this authentication path from regressing silently.

🤖 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/server/custom-token-exchange.test.ts` around lines 1225 - 1391, Add a
test in the “5.3: wire parameters” suite for requestSessionTransferToken that
mocks an initial token-endpoint response requiring use_dpop_nonce, then verifies
the retry includes the returned DPoP nonce and succeeds with the expected token
response. Use the existing authClient, session setup, and server request
interception patterns, and assert both the retry nonce and successful result.
🤖 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/with-cte/app/api/stt/route.ts`:
- Around line 41-72: Validate targetLoginUrl in the route handling around
auth0.buildSessionTransferRedirect against an environment-configured allowlist
of trusted target applications, returning 400 for malformed or disallowed URLs
before forwarding it. In examples/with-cte/app/api/stt/route.ts lines 41-72,
apply this validation at the existing URL-parsing point; in
examples/with-cte/README.md lines 98-107, document that targetLoginUrl must be
trusted and app-controlled and is restricted by this demo’s allowlist.
- Around line 41-46: Validate targetLoginUrl before passing it to
buildSessionTransferRedirect: parse it explicitly and require it to match a
small env-configured allowlist of trusted target applications, rejecting
malformed or non-allowlisted values with the existing 400 invalid_request
response. Update the route’s targetLoginUrl handling and the corresponding
redirect flow so no untrusted URL can receive the session transfer token,
including the additionally referenced lines.
- Around line 79-97: Update the status selection in the CustomTokenExchangeError
handling block to remove the dead ternary and assign status codes intentionally
by error type. Keep ACTOR_UNAVAILABLE as a 400 client/session error, and map
tenant or upstream configuration failures such as SETACTOR_REQUIRED,
SESSION_TRANSFER_DISABLED, and EXCHANGE_FAILED to the appropriate server or
upstream status.

In `@examples/with-cte/README.md`:
- Around line 98-107: Add a one-line warning to the Session Transfer
walkthrough’s step 3 that targetLoginUrl must be a trusted, app-controlled
endpoint and must not come from untrusted input. Also update the targetLoginUrl
validation in buildSessionTransferRedirect or the corresponding
app/api/stt/route.ts flow to enforce trusted destinations, preserving valid
registered CTE target URLs.

In `@src/server/auth-client.ts`:
- Around line 3473-3498: Correct the actor-refresh documentation to match the
fail-fast behavior of resolveActorFromSession: in src/server/auth-client.ts
lines 3473-3498 and src/server/client.ts lines 1237-1258, state that an expired
agent ID token throws ACTOR_UNAVAILABLE and must be refreshed by the caller or
replaced with options.actor; in src/errors/oauth-errors.ts lines 226-231, state
that any expired ID token triggers ACTOR_UNAVAILABLE regardless of refresh-token
availability.

In `@src/server/custom-token-exchange.test.ts`:
- Around line 1394-1416: Update the affected session-transfer-token tests,
including the test around “should not include the STT in any session-like field
on the result” and the corresponding case near the second referenced location,
to assert that the exchange succeeded and result is non-null before inspecting
its keys or properties. Keep the existing contract assertions unchanged after
this success check.

---

Nitpick comments:
In `@src/server/custom-token-exchange.test.ts`:
- Around line 1225-1391: Add a test in the “5.3: wire parameters” suite for
requestSessionTransferToken that mocks an initial token-endpoint response
requiring use_dpop_nonce, then verifies the retry includes the returned DPoP
nonce and succeeds with the expected token response. Use the existing
authClient, session setup, and server request interception patterns, and assert
both the retry nonce and successful result.

In `@src/types/token-vault.ts`:
- Around line 235-244: Narrow SessionTransferTokenResult.issuedTokenType from
string to the specific TOKEN_TYPES.SESSION_TRANSFER_TOKEN type (or its
corresponding literal type), preserving the documented invariant so consumers
receive compile-time protection when branching on this field.

In `@src/utils/session-transfer-helpers.ts`:
- Around line 117-133: Tighten mapSttServerError to classify only the exact,
documented Auth0 CTE server error codes for setActor-required and
session-transfer-disabled cases. Remove broad substring matching, use an
explicit allow-list if multiple known variants exist, and return null for all
unrelated or unknown error codes.
🪄 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: 166a97d2-3f16-43fd-8dde-ff9b01ea7e52

📥 Commits

Reviewing files that changed from the base of the PR and between 897ba7c and e85d890.

⛔ Files ignored due to path filters (1)
  • examples/with-cte/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • EXAMPLES.md
  • examples/with-cte/README.md
  • examples/with-cte/app/api/stt/route.ts
  • examples/with-cte/app/dashboard/page.tsx
  • examples/with-cte/app/stt/page.tsx
  • examples/with-cte/app/stt/session-transfer-form.tsx
  • examples/with-cte/next-env.d.ts
  • examples/with-cte/pnpm-workspace.yaml
  • 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

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 6

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

117-133: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Substring matching on server error codes may over-classify unrelated errors.

errorCode.toLowerCase().includes("setactor") / .includes("session_transfer") will reclassify any future/unexpected error code that happens to contain those substrings (e.g. a generic error mentioning "session_transfer" in a description-derived code) as SETACTOR_REQUIRED/SESSION_TRANSFER_DISABLED, masking the real EXCHANGE_FAILED case callers would otherwise get. Worth confirming this matches the actual set of Auth0 CTE server error codes, or tightening to an allow-list of exact known codes.

Can you confirm the exact set of server-side error codes returned for the setActor-required and session_transfer disabled cases (from the Auth0 CTE Action / tenant configuration), to validate whether substring matching is actually necessary here?

🤖 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 117 - 133, Tighten
mapSttServerError to classify only the exact, documented Auth0 CTE server error
codes for setActor-required and session-transfer-disabled cases. Remove broad
substring matching, use an explicit allow-list if multiple known variants exist,
and return null for all unrelated or unknown error codes.
src/types/token-vault.ts (1)

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

Consider narrowing issuedTokenType's type to match its documented contract.

The doc comment states issuedTokenType is "Always TOKEN_TYPES.SESSION_TRANSFER_TOKEN. Branch on this, not tokenType," but the field is typed as plain string, so TypeScript won't catch typos or incorrect comparisons when consumers branch on it.

♻️ Suggested tightening
-  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, Narrow
SessionTransferTokenResult.issuedTokenType from string to the specific
TOKEN_TYPES.SESSION_TRANSFER_TOKEN type (or its corresponding literal type),
preserving the documented invariant so consumers receive compile-time protection
when branching on this field.
src/server/custom-token-exchange.test.ts (1)

1225-1391: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add STT-specific DPoP nonce retry coverage.

STT bypasses the standard token-response processor and uses a bespoke retry path. Test a first use_dpop_nonce response followed by a successful nonce-bearing request to prevent this authentication path from regressing silently.

🤖 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/server/custom-token-exchange.test.ts` around lines 1225 - 1391, Add a
test in the “5.3: wire parameters” suite for requestSessionTransferToken that
mocks an initial token-endpoint response requiring use_dpop_nonce, then verifies
the retry includes the returned DPoP nonce and succeeds with the expected token
response. Use the existing authClient, session setup, and server request
interception patterns, and assert both the retry nonce and successful result.
🤖 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/with-cte/app/api/stt/route.ts`:
- Around line 41-72: Validate targetLoginUrl in the route handling around
auth0.buildSessionTransferRedirect against an environment-configured allowlist
of trusted target applications, returning 400 for malformed or disallowed URLs
before forwarding it. In examples/with-cte/app/api/stt/route.ts lines 41-72,
apply this validation at the existing URL-parsing point; in
examples/with-cte/README.md lines 98-107, document that targetLoginUrl must be
trusted and app-controlled and is restricted by this demo’s allowlist.
- Around line 41-46: Validate targetLoginUrl before passing it to
buildSessionTransferRedirect: parse it explicitly and require it to match a
small env-configured allowlist of trusted target applications, rejecting
malformed or non-allowlisted values with the existing 400 invalid_request
response. Update the route’s targetLoginUrl handling and the corresponding
redirect flow so no untrusted URL can receive the session transfer token,
including the additionally referenced lines.
- Around line 79-97: Update the status selection in the CustomTokenExchangeError
handling block to remove the dead ternary and assign status codes intentionally
by error type. Keep ACTOR_UNAVAILABLE as a 400 client/session error, and map
tenant or upstream configuration failures such as SETACTOR_REQUIRED,
SESSION_TRANSFER_DISABLED, and EXCHANGE_FAILED to the appropriate server or
upstream status.

In `@examples/with-cte/README.md`:
- Around line 98-107: Add a one-line warning to the Session Transfer
walkthrough’s step 3 that targetLoginUrl must be a trusted, app-controlled
endpoint and must not come from untrusted input. Also update the targetLoginUrl
validation in buildSessionTransferRedirect or the corresponding
app/api/stt/route.ts flow to enforce trusted destinations, preserving valid
registered CTE target URLs.

In `@src/server/auth-client.ts`:
- Around line 3473-3498: Correct the actor-refresh documentation to match the
fail-fast behavior of resolveActorFromSession: in src/server/auth-client.ts
lines 3473-3498 and src/server/client.ts lines 1237-1258, state that an expired
agent ID token throws ACTOR_UNAVAILABLE and must be refreshed by the caller or
replaced with options.actor; in src/errors/oauth-errors.ts lines 226-231, state
that any expired ID token triggers ACTOR_UNAVAILABLE regardless of refresh-token
availability.

In `@src/server/custom-token-exchange.test.ts`:
- Around line 1394-1416: Update the affected session-transfer-token tests,
including the test around “should not include the STT in any session-like field
on the result” and the corresponding case near the second referenced location,
to assert that the exchange succeeded and result is non-null before inspecting
its keys or properties. Keep the existing contract assertions unchanged after
this success check.

---

Nitpick comments:
In `@src/server/custom-token-exchange.test.ts`:
- Around line 1225-1391: Add a test in the “5.3: wire parameters” suite for
requestSessionTransferToken that mocks an initial token-endpoint response
requiring use_dpop_nonce, then verifies the retry includes the returned DPoP
nonce and succeeds with the expected token response. Use the existing
authClient, session setup, and server request interception patterns, and assert
both the retry nonce and successful result.

In `@src/types/token-vault.ts`:
- Around line 235-244: Narrow SessionTransferTokenResult.issuedTokenType from
string to the specific TOKEN_TYPES.SESSION_TRANSFER_TOKEN type (or its
corresponding literal type), preserving the documented invariant so consumers
receive compile-time protection when branching on this field.

In `@src/utils/session-transfer-helpers.ts`:
- Around line 117-133: Tighten mapSttServerError to classify only the exact,
documented Auth0 CTE server error codes for setActor-required and
session-transfer-disabled cases. Remove broad substring matching, use an
explicit allow-list if multiple known variants exist, and return null for all
unrelated or unknown error codes.
🪄 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: 166a97d2-3f16-43fd-8dde-ff9b01ea7e52

📥 Commits

Reviewing files that changed from the base of the PR and between 897ba7c and e85d890.

⛔ Files ignored due to path filters (1)
  • examples/with-cte/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • EXAMPLES.md
  • examples/with-cte/README.md
  • examples/with-cte/app/api/stt/route.ts
  • examples/with-cte/app/dashboard/page.tsx
  • examples/with-cte/app/stt/page.tsx
  • examples/with-cte/app/stt/session-transfer-form.tsx
  • examples/with-cte/next-env.d.ts
  • examples/with-cte/pnpm-workspace.yaml
  • 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
🛑 Comments failed to post (6)
examples/with-cte/app/api/stt/route.ts (3)

41-46: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

targetLoginUrl is untrusted client input but is used exactly as the SDK warns against.

body.targetLoginUrl comes straight from the request body and is passed unchecked into buildSessionTransferRedirect, which embeds the freshly-minted, one-shot Session Transfer Token into the redirect Location header for that URL. The SDK's own docs for buildSessionTransferRedirect state the target "Must be a trusted, app-controlled value. Never derive this from untrusted input." — this route does precisely that, meaning a caller can redirect (and leak) a live impersonation token to any domain of their choosing. Separately, a malformed URL here throws inside new URL(...), which isn't a CustomTokenExchangeError, so it falls into the generic 500 handler instead of a 400.

Restrict targetLoginUrl to a small allowlist (e.g. an env-configured set of trusted target apps) and validate it explicitly before use, returning 400 for both "not in allowlist" and "malformed URL".

🔒 Suggested validation
+const ALLOWED_TARGET_LOGIN_URLS = (process.env.ALLOWED_TARGET_LOGIN_URLS ?? "")
+  .split(",")
+  .map((u) => u.trim())
+  .filter(Boolean);
+
   if (!body.targetLoginUrl) {
     return NextResponse.json(
       { code: "invalid_request", message: "targetLoginUrl is required." },
       { status: 400 }
     );
   }
+
+  let targetUrl: URL;
+  try {
+    targetUrl = new URL(body.targetLoginUrl);
+  } catch {
+    return NextResponse.json(
+      { code: "invalid_request", message: "targetLoginUrl must be a valid URL." },
+      { status: 400 }
+    );
+  }
+  if (!ALLOWED_TARGET_LOGIN_URLS.includes(targetUrl.origin)) {
+    return NextResponse.json(
+      { code: "invalid_request", message: "targetLoginUrl is not an allowed target." },
+      { status: 400 }
+    );
+  }

Also applies to: 63-72

🤖 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 `@examples/with-cte/app/api/stt/route.ts` around lines 41 - 46, Validate
targetLoginUrl before passing it to buildSessionTransferRedirect: parse it
explicitly and require it to match a small env-configured allowlist of trusted
target applications, rejecting malformed or non-allowlisted values with the
existing 400 invalid_request response. Update the route’s targetLoginUrl
handling and the corresponding redirect flow so no untrusted URL can receive the
session transfer token, including the additionally referenced lines.

41-72: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

targetLoginUrl is treated as trusted input in both the code and the walkthrough that teaches it, contradicting the SDK's own security warning. The SDK's buildSessionTransferRedirect docs state the target URL "must be a trusted, app-controlled value" and must never come from untrusted input, yet the demo takes it directly from the client request body/form with no validation or caveat, letting a caller redirect (and leak) a live one-shot impersonation token to an arbitrary destination.

  • examples/with-cte/app/api/stt/route.ts#L41-L72: validate targetLoginUrl against an allowlist (e.g. an env-configured set of trusted target apps) and return 400 for both malformed and disallowed values, instead of forwarding it unchecked into buildSessionTransferRedirect.
  • examples/with-cte/README.md#L98-L107: add a caveat in the walkthrough noting that targetLoginUrl must be a trusted, app-controlled value and is restricted in this demo for that reason.
📍 Affects 2 files
  • examples/with-cte/app/api/stt/route.ts#L41-L72 (this comment)
  • examples/with-cte/README.md#L98-L107
🤖 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 `@examples/with-cte/app/api/stt/route.ts` around lines 41 - 72, Validate
targetLoginUrl in the route handling around auth0.buildSessionTransferRedirect
against an environment-configured allowlist of trusted target applications,
returning 400 for malformed or disallowed URLs before forwarding it. In
examples/with-cte/app/api/stt/route.ts lines 41-72, apply this validation at the
existing URL-parsing point; in examples/with-cte/README.md lines 98-107,
document that targetLoginUrl must be trusted and app-controlled and is
restricted by this demo’s allowlist.

79-97: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Dead ternary — both branches return the same status.

err.code === CustomTokenExchangeErrorCode.ACTOR_UNAVAILABLE ? 400 : 400 always evaluates to 400; the conditional has no effect. This reads like an incomplete edit — likely meant to differentiate ACTOR_UNAVAILABLE (client/session issue → 400) from tenant-config failures like SETACTOR_REQUIRED/SESSION_TRANSFER_DISABLED/EXCHANGE_FAILED (arguably 500/502, since they indicate upstream Action/tenant misconfiguration rather than bad client input).

🐛 Suggested fix
-      const status =
-        err.code === CustomTokenExchangeErrorCode.ACTOR_UNAVAILABLE ? 400 : 400;
+      const status =
+        err.code === CustomTokenExchangeErrorCode.EXCHANGE_FAILED ||
+        err.code === CustomTokenExchangeErrorCode.SETACTOR_REQUIRED ||
+        err.code === CustomTokenExchangeErrorCode.SESSION_TRANSFER_DISABLED
+          ? 500
+          : 400;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  } catch (err: unknown) {
    if (err instanceof CustomTokenExchangeError) {
      const status =
        err.code === CustomTokenExchangeErrorCode.EXCHANGE_FAILED ||
        err.code === CustomTokenExchangeErrorCode.SETACTOR_REQUIRED ||
        err.code === CustomTokenExchangeErrorCode.SESSION_TRANSFER_DISABLED
          ? 500
          : 400;
      return NextResponse.json(
        {
          code: err.code,
          message: err.message,
          cause:
            err.cause && typeof err.cause === "object" && "code" in err.cause
              ? {
                  code: (err.cause as { code?: string }).code,
                  message: (err.cause as { message?: string }).message
                }
              : undefined
        },
        { status }
      );
    }
🤖 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 `@examples/with-cte/app/api/stt/route.ts` around lines 79 - 97, Update the
status selection in the CustomTokenExchangeError handling block to remove the
dead ternary and assign status codes intentionally by error type. Keep
ACTOR_UNAVAILABLE as a 400 client/session error, and map tenant or upstream
configuration failures such as SETACTOR_REQUIRED, SESSION_TRANSFER_DISABLED, and
EXCHANGE_FAILED to the appropriate server or upstream status.
examples/with-cte/README.md (1)

98-107: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Walkthrough doesn't warn that targetLoginUrl must be a trusted value.

Step 3 tells the reader to type any target login URL into the form, with no note that this must be a trusted, app-controlled endpoint (the SDK's own buildSessionTransferRedirect docs explicitly warn against deriving it from untrusted input). Worth a one-line caveat here, alongside fixing the underlying validation gap in app/api/stt/route.ts.

🤖 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 `@examples/with-cte/README.md` around lines 98 - 107, Add a one-line warning to
the Session Transfer walkthrough’s step 3 that targetLoginUrl must be a trusted,
app-controlled endpoint and must not come from untrusted input. Also update the
targetLoginUrl validation in buildSessionTransferRedirect or the corresponding
app/api/stt/route.ts flow to enforce trusted destinations, preserving valid
registered CTE target URLs.
src/server/auth-client.ts (1)

3473-3498: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Actor-refresh documentation is inconsistent with the actual (fail-fast) behavior across three files. All three sites describe requestSessionTransferToken's actor resolution as if an expired agent ID token gets auto-refreshed, but resolveActorFromSession (per the provided helper source) only checks the current session's exp claim and immediately throws ACTOR_UNAVAILABLE telling the caller to refresh manually — there is no refresh attempt, regardless of refresh-token availability.

  • src/server/auth-client.ts#L3473-L3498: reword line 3481 ("The actor is sourced from the agent session's ID token (refreshed if expired).") to state that an expired ID token throws ACTOR_UNAVAILABLE and must be refreshed by the caller or overridden via options.actor.
  • src/server/client.ts#L1237-L1258: reword line 1242 ("The actor defaults to the agent session's ID token (refreshed if expired).") the same way.
  • src/errors/oauth-errors.ts#L226-L231: reword the ACTOR_UNAVAILABLE doc's "missing or expired with no refresh token" phrasing to avoid implying refresh-token-conditional auto-refresh; state plainly that any expired ID token triggers this error regardless of refresh-token presence.
📍 Affects 3 files
  • src/server/auth-client.ts#L3473-L3498 (this comment)
  • src/server/client.ts#L1237-L1258
  • src/errors/oauth-errors.ts#L226-L231
🤖 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/server/auth-client.ts` around lines 3473 - 3498, Correct the
actor-refresh documentation to match the fail-fast behavior of
resolveActorFromSession: in src/server/auth-client.ts lines 3473-3498 and
src/server/client.ts lines 1237-1258, state that an expired agent ID token
throws ACTOR_UNAVAILABLE and must be refreshed by the caller or replaced with
options.actor; in src/errors/oauth-errors.ts lines 226-231, state that any
expired ID token triggers ACTOR_UNAVAILABLE regardless of refresh-token
availability.
src/server/custom-token-exchange.test.ts (1)

1394-1416: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert success before checking absent properties.

Both tests can pass with a null result, so exchange failures would not fail the intended contract checks.

Proposed fix
-const [, result] = await authClient.requestSessionTransferToken(
+const [error, result] = await authClient.requestSessionTransferToken(
   ...
 );
+expect(error).toBeNull();
+expect(result).not.toBeNull();

-const [, response] = await authClient.customTokenExchange({
+const [error, response] = await authClient.customTokenExchange({
   ...
 });
+expect(error).toBeNull();
+expect(response).not.toBeNull();

Also applies to: 1629-1636

🤖 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/server/custom-token-exchange.test.ts` around lines 1394 - 1416, Update
the affected session-transfer-token tests, including the test around “should not
include the STT in any session-like field on the result” and the corresponding
case near the second referenced location, to assert that the exchange succeeded
and result is non-null before inspecting its keys or properties. Keep the
existing contract assertions unchanged after this success check.

@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/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 Outdated
Comment thread src/server/auth-client.ts
Comment thread src/server/auth-client.ts Outdated
Comment thread src/utils/session-transfer-helpers.ts Outdated
@Piyush-85
Piyush-85 force-pushed the feat/cte-stt-example branch from 10c6841 to 670003f Compare July 21, 2026 09:54
Comment thread examples/with-cte/app/api/stt/route.ts
Comment thread examples/with-cte/app/api/stt/route.ts Outdated
Comment thread examples/with-cte/README.md
nandan-bhat
nandan-bhat previously approved these changes Jul 22, 2026
Comment thread examples/with-cte/app/dashboard/page.tsx Outdated
Comment thread examples/with-cte/app/api/stt/route.ts Outdated
Comment thread examples/with-cte/app/api/stt/route.ts Outdated
Comment thread examples/with-cte/app/api/stt/route.ts Outdated
@Piyush-85
Piyush-85 force-pushed the feat/cte-stt-example branch from 0aecb22 to 45eb74f Compare July 22, 2026 11:47
@Piyush-85
Piyush-85 requested a review from kishore7snehil July 22, 2026 11:49
Base automatically changed from feat/cte-session-transfer-token to main July 22, 2026 12:24
@Piyush-85
Piyush-85 dismissed nandan-bhat’s stale review July 22, 2026 12:24

The base branch was changed.

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

🤖 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/with-cte/app/api/stt/route.ts`:
- Around line 46-64: Validate that the parsed result in the request-body
handling flow is a non-null object before accessing subjectToken or
subjectTokenType. Return the existing invalid_request 400 response for null and
other non-object JSON bodies, while preserving the current required-field
validation for valid objects.
- Around line 137-164: Update the error branch in the route handler to classify
errors by a guarded object check and string-valued error.code, rather than
instanceof CustomTokenExchangeError. Use the validated code to map
CustomTokenExchangeErrorCode values to the existing 400/500 statuses, while
preserving the current response fields and cause handling.
🪄 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: eab257a8-5bed-408c-a34a-6f5085892c7b

📥 Commits

Reviewing files that changed from the base of the PR and between e85d890 and bce6f76.

📒 Files selected for processing (6)
  • examples/with-cte/.gitignore
  • examples/with-cte/README.md
  • examples/with-cte/app/api/stt/route.ts
  • examples/with-cte/app/dashboard/page.tsx
  • examples/with-cte/app/stt/page.tsx
  • examples/with-cte/app/stt/session-transfer-form.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • examples/with-cte/app/stt/page.tsx
  • examples/with-cte/app/stt/session-transfer-form.tsx
  • examples/with-cte/README.md

Comment thread examples/with-cte/app/api/stt/route.ts
Comment thread examples/with-cte/app/api/stt/route.ts
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.

3 participants