Skip to content

feat(customTokenExchange): promote actor_token, organization, requested_token_type to first-class options#834

Open
cschetan77 wants to merge 2 commits into
masterfrom
feat/cte-enhancements
Open

feat(customTokenExchange): promote actor_token, organization, requested_token_type to first-class options#834
cschetan77 wants to merge 2 commits into
masterfrom
feat/cte-enhancements

Conversation

@cschetan77

@cschetan77 cschetan77 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Changes

lib/context.js

  • Promote actor_token, actor_token_type, requested_token_type, and organization from extra passthrough to named first-class options on req.oidc.customTokenExchange()
  • Add all four to PARAM_DENYLISTactor_token/actor_token_type must be paired and validated together; organization affects tenant context and must be explicit; requested_token_type has a dedicated field to make intent clear
  • Validate that actor_token_type is required when actor_token is provided, throwing HTTP 400 before hitting the network
  • Add extractActClaim() helper that extracts the act claim from delegation exchange responses — checks id_token claims first (via openid-client's claims() helper), falls back to decoding the JWT access token for M2M flows, silently skips opaque tokens
  • Attach act claim to the returned result when actor_token was provided

index.d.ts

  • Add ActClaim interface ({ sub: string; [key: string]: unknown })
  • Add actor_token, actor_token_type, requested_token_type, organization to CustomTokenExchangeOptions
  • Add act?: ActClaim to TokenExchangeResponse
  • Update extra type to accept string[] values (consistent with @auth0/auth0-auth-js)

Updated usage

// Delegation / impersonation
const tokenSet = await req.oidc.customTokenExchange({
  actor_token: agentAccessToken,
  actor_token_type: 'urn:acme:agent-token',
  audience: 'https://api.example.com',
});
console.log(tokenSet.act); // { sub: '<agent-sub>' }

// Targeted downstream API with org context
const tokenSet = await req.oidc.customTokenExchange({
  audience: 'https://inventory.example.com',
  scope: 'read:stock',
  organization: 'org_abc123',
  requested_token_type: 'urn:ietf:params:oauth:token-type:jwt',
});

Unit tests updated

  • Migrated organization test from extra to first-class option
  • Replaced old catch-all extra actor test with dedicated first-class tests for actor_token/actor_token_type and requested_token_type
  • Added validation test: actor_token without actor_token_type → HTTP 400
  • Added act claim extraction tests: extracted when actor_token provided, absent when it is not

Test plan

  • npm test — all 283 tests pass, no regressions
  • All 17 customTokenExchange tests pass including 6 new/updated cases

…ed_token_type to first-class options

- Promote `actor_token`, `actor_token_type`, `requested_token_type`, and
  `organization` from `extra` passthrough to named first-class options on
  `customTokenExchange`, consistent with the CTE implementation in
  `@auth0/auth0-auth-js` and `@auth0/auth0-server-js`
- Add all four to `PARAM_DENYLIST` so they cannot be accidentally double-set
  via `extra` (actor_token/type must be paired and validated together;
  organization affects tenant context and must be explicit)
- Validate that `actor_token_type` is required when `actor_token` is provided,
  throwing HTTP 400 before hitting the network
- Extract and expose the `act` claim on the response for delegation flows:
  checks `id_token` claims first (via openid-client's `claims()` helper),
  falls back to decoding the JWT access token, silently skips opaque tokens
- Add `ActClaim` interface and `act` field to `TokenExchangeResponse` in
  `index.d.ts`; update `extra` type to accept `string[]` values
- Update unit tests: migrate existing actor/organization tests to first-class
  options, add coverage for `requested_token_type`, `actor_token_type`
  validation, and `act` claim extraction
@cschetan77 cschetan77 requested a review from a team as a code owner June 23, 2026 08:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant