How the exchangeToken extra Parameter Works in Auth0 Auth JS: Security Risks and Implementation

The extra parameter in exchangeToken allows developers to inject custom metadata into OAuth 2.0 token exchange requests, but carries risks of data leakage and parameter injection that the SDK mitigates through a reserved parameter deny-list and strict array size limits.

The auth0-auth-js library provides the exchangeToken method to perform RFC 8693 token exchanges and Token Vault operations. When you need to pass custom data to Auth0 Actions or downstream services, the extra parameter in ExchangeProfileOptions accepts a record of string values, though it comes with specific security guardrails implemented in the SDK source code.

How the extra Parameter Works in exchangeToken

Type Definition and Documentation

In packages/auth0-auth-js/src/types.ts (lines 283-370), the extra field is defined within ExchangeProfileOptions as Record<string, string | string[]>. The JSDoc explicitly warns that this field cannot override reserved OAuth parameters and advises developers never to include PII or secrets because these values may appear in audit logs or network traces. Array values are limited to 20 items per key to prevent request bloat.

The Parameter Deny-List

To prevent attackers from overriding critical OAuth parameters, the SDK maintains a PARAM_DENYLIST set in packages/auth0-auth-js/src/auth-client.ts (lines 91-113). This deny-list contains reserved keys such as grant_type, client_id, client_secret, scope, audience, and resource. Any key in extra matching this set is silently discarded before the request is sent.

Appending Extra Parameters

The appendExtraParams function in packages/auth0-auth-js/src/auth-client.ts (lines 144-162) processes the extra object during exchangeToken execution. The function iterates over each key-value pair and applies three rules: it skips keys found in PARAM_DENYLIST, validates that arrays do not exceed the MAX_ARRAY_VALUES_PER_KEY limit of 20 items (throwing TokenExchangeError if exceeded), and appends valid entries to the request's URLSearchParams. The resulting parameters serialize as application/x-www-form-urlencoded data sent to the Auth0 endpoint.

Server-Side Availability

Once the request reaches Auth0, parameters passed through extra become accessible in Auth0 Actions under event.request.body. This enables custom business logic such as device fingerprinting or session tracking based on the metadata you provide.

Security Risks of the extra Parameter

Sensitive Data Leakage

Values sent via extra may be written to audit logs or appear in network traces. The SDK documentation explicitly warns against including passwords, secrets, or personally identifiable information (PII) in this field, as there is no technical enforcement preventing such data from being transmitted.

OAuth Parameter Injection

Without the SDK's protections, an attacker could attempt to override reserved parameters like scope or grant_type to escalate privileges. The PARAM_DENYLIST in auth-client.ts mitigates this by silently stripping any attempt to override these reserved keys. Unit tests in packages/auth0-auth-js/src/auth-client.spec.ts verify that provided scope values in extra are ignored while explicitly passed parameters win.

Denial of Service via Large Arrays

An attacker could attempt to cause memory pressure or request-size blow-up by sending massive arrays. The SDK enforces a hard limit of 20 items per array key via MAX_ARRAY_VALUES_PER_KEY. Exceeding this limit throws a TokenExchangeError, preventing resource exhaustion attacks.

Practical Code Examples

The following examples demonstrate safe usage, the deny-list behavior, and array limits:

import { AuthClient } from '@auth0/auth0-auth-js';

const authClient = new AuthClient({
  domain: 'my-tenant.auth0.com',
  clientId: '<client_id>',
  clientSecret: '<client_secret>',
});

/**
 * Example 1 – Adding safe custom metadata.
 */
await authClient.exchangeToken({
  subjectTokenType: 'urn:acme:legacy-token',
  subjectToken: 'legacy-token-xyz',
  audience: 'https://api.example.com',
  scope: 'openid profile',
  extra: {
    // These will be visible in an Auth0 Action as event.request.body.device_id, etc.
    device_id: 'device-12345',
    session_id: 'sess-abcde',
  },
});

/**
 * Example 2 – Attempting to override a reserved parameter (will be ignored).
 */
await authClient.exchangeToken({
  subjectTokenType: 'urn:acme:legacy-token',
  subjectToken: 'legacy-token-xyz',
  audience: 'https://api.example.com',
  scope: 'openid profile',
  extra: {
    // `scope` is on the deny-list → the SDK discards it.
    scope: 'should_be_ignored',
    // Custom param is allowed.
    custom_param: 'allowed',
  },
});

/**
 * Example 3 – Exceeding the array limit (throws TokenExchangeError).
 */
await authClient.exchangeToken({
  subjectTokenType: 'urn:acme:legacy-token',
  subjectToken: 'legacy-token-xyz',
  audience: 'https://api.example.com',
  extra: {
    // 21 items > max 20 → error.
    large_array: Array.from({ length: 21 }, (_, i) => `value${i}`),
  },
});

Summary

  • The extra parameter in exchangeToken accepts custom metadata as Record<string, string | string[]> defined in types.ts.
  • Reserved parameters like scope and grant_type are protected by a PARAM_DENYLIST in auth-client.ts that silently strips overrides.
  • Arrays in extra are limited to 20 items to prevent DoS attacks; exceeding this throws TokenExchangeError.
  • Never include PII or secrets in extra values, as they may appear in Auth0 audit logs or network traces.
  • Custom parameters arrive in Auth0 Actions via event.request.body for business logic processing.

Frequently Asked Questions

Can the extra parameter override standard OAuth parameters like scope or audience?

No. The SDK maintains a PARAM_DENYLIST in packages/auth0-auth-js/src/auth-client.ts (lines 91-113) that explicitly prevents overriding reserved OAuth parameters. If you attempt to pass scope or audience inside extra, the appendExtraParams function silently discards these keys before sending the request to Auth0.

What happens if I send an array with more than 20 items in the extra parameter?

The SDK throws a TokenExchangeError. The constant MAX_ARRAY_VALUES_PER_KEY is set to 20 in packages/auth0-auth-js/src/auth-client.ts. When appendExtraParams detects an array exceeding this limit, it halts execution and raises an error to prevent request size attacks and memory exhaustion.

Is it safe to pass sensitive data like passwords or API keys through the extra parameter?

No. According to the JSDoc in packages/auth0-auth-js/src/types.ts, you should never include PII or secrets in the extra parameter. These values may be logged in Auth0 audit trails, appear in network traces, or be stored in plaintext server-side. Use extra only for non-sensitive metadata like device IDs or session identifiers.

How do I access extra parameters in my Auth0 Action?

Auth0 exposes custom parameters from extra in the event.request.body object within your Action code. For example, if you send extra: { device_id: 'abc123' }, you can retrieve it in your Action using event.request.body.device_id to implement custom authentication logic or risk assessments.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →