Skip to content

feat(auth0-express-api):add support for on-behalf-of token exchange - #40

Open
nandan-bhat wants to merge 3 commits into
mainfrom
feat/sdk-8431-api-as-client-obo
Open

feat(auth0-express-api):add support for on-behalf-of token exchange#40
nandan-bhat wants to merge 3 commits into
mainfrom
feat/sdk-8431-api-as-client-obo

Conversation

@nandan-bhat

Copy link
Copy Markdown
Contributor

What

auth0-express-api can now exchange the caller's access token for one issued to a downstream API, still representing the same user. This is On-Behalf-Of token exchange (RFC 8693).

app.get('/orders', requiresAuth(), async (req, res) => {
  const { accessToken } = await req.auth0.getTokenOnBehalfOf!({
    audience: 'https://orders.example.com',
    scope: 'read:orders',
  });

  const orders = await fetch('https://orders.example.com/orders', {
    headers: { authorization: `Bearer ${accessToken}` },
  });

  res.json(await orders.json());
});

Why

Today an app that wants this has to add @auth0/auth0-api-js as a direct dependency, build its own ApiClient, and pull the raw token off the request itself. The token was not even available on req.auth0, so there was nothing to exchange.

Notes for reviewers

The exchanged token defaults to the verified one. You do not pass it in. That is why the route needs requiresAuth(). An optional subjectToken covers the case where the token comes from somewhere else, such as one held for a background job. Making the caller always supply a token would invite passing an unverified one.

Startup validation is a behavior change. createAuth0Api() now throws when clientId is set without clientSecret or clientAssertionSigningKey, and when a credential is set without a clientId. An app that only verifies tokens but has a stray AUTH0_CLIENT_ID in a shared .env will now fail to boot. Deliberate: clientId alone can never work server side, and failing at startup beats failing on a real request in production.

getTokenOnBehalfOf is optional on req.auth0, so call sites need !. Making it required breaks the as Partial<Request> mock pattern that five spec files in this package rely on (TS2741). This is inconsistent with auth0-express, where req.auth0.client is required. Worth settling for both packages, separately from this PR.

Testing

New specs for requiresAuth() (its first unit tests), for the exchange helper, and for the public export surface. The integration tests assert the exact token exchange request body sent to the tenant.

@nandan-bhat nandan-bhat changed the title Feat/sdk 8431 api as client obo feat(auth0-express-api):add support for on-behalf-of token exchange Aug 15, 2026
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