> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tesouro.com/llms.txt
> Use this file to discover all available pages before exploring further.

# User token

> Authenticate users via token exchange for user impersonation and widget authentication.

User token authentication lets your application act on behalf of a specific user. It connects to a dedicated OIDC application (such as Nail It or another VSP), and the resulting token is scoped to that user's permissions only.

This flow has two parts: **token exchange** for API access, and **widget tokens** for embedded UI components.

For the conceptual model — what a user is, how `sub` and `email` claims map to a Tesouro identity, when to issue one token shape versus the other, and how to add users beyond the primary controller — see the [Users guide](/embedded-banking/guides/users/overview).

## Token exchange for user impersonation

Tesouro supports [OAuth 2.0 Token Exchange (RFC 8693)](https://datatracker.ietf.org/doc/html/rfc8693). Your application exchanges a user JWT from your OIDC provider for a Tesouro access token scoped to that user.

```bash title="Request" lines theme={null}
curl --location 'https://api.sandbox.tesouro.com/openid/connect/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
  --data-urlencode 'client_id=CLIENT_ID' \
  --data-urlencode 'client_secret=CLIENT_SECRET' \
  --data-urlencode 'subject_token=USER_JWT_TOKEN' \
  --data-urlencode 'subject_token_type=urn:ietf:params:oauth:token-type:access_token'
```

```json title="Response" lines theme={null}
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

<Info>
  Your JWT must include the user's unique identifier in the `sub` claim and their email in the
  `email` claim.
</Info>

<Danger>
  Users can only be impersonated within their assigned application context. Cross-application
  impersonation is prevented.
</Danger>

## Widget authentication

For embedded widgets, your backend generates a **widget token** — an encrypted [JWE (RFC 7516)](https://datatracker.ietf.org/doc/html/rfc7516) that securely wraps the user's identity and OAuth credentials. This token is passed to the widget on the frontend.

Your backend must provide:

* The user's identity (ID and email)
* The applicant's organization reference — your opaque, stable identifier for the business the applicant belongs to
* Your OAuth client credentials
* A token expiration that matches your security requirements

```typescript title="Widget token generation" lines expandable theme={null}
import { EncryptJWT } from "jose";

async function createWidgetToken(userId: string, userEmail: string, organizationReference: string) {
  const clientId = process.env.TESOURO_CLIENT_ID;
  const clientSecret = process.env.TESOURO_CLIENT_SECRET;
  const sharedSecret = process.env.TESOURO_WIDGET_SECRET; // 32 bytes

  const now = Math.floor(Date.now() / 1000);
  const secretKey = new TextEncoder().encode(sharedSecret);

  // Step 1: Create subject token (user identity)
  const subjectTokenPayload = {
    sub: userId,
    email: userEmail,
    aud: "tesouro",
    iss: clientId,
    exp: now + 300,
    iat: now,
  };

  const subjectToken =
    Buffer.from(JSON.stringify({ alg: "none", typ: "JWT" })).toString("base64url") +
    "." +
    Buffer.from(JSON.stringify(subjectTokenPayload)).toString("base64url") +
    ".";

  // Step 2: Create encrypted widget token
  const payload = {
    aud: "tesouro",
    client_id: clientId,
    client_secret: clientSecret,
    exp: now + 300,
    grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
    iat: now,
    iss: clientId,
    jti: crypto.randomUUID(),
    organization_reference: organizationReference, // your stable id for the applicant's business
    subject_token: subjectToken,
    subject_token_type: "urn:ietf:params:oauth:token-type:access_token",
  };

  const token = await new EncryptJWT(payload)
    .setProtectedHeader({
      alg: "A256KW",
      enc: "A256GCM",
      kid: clientId,
      typ: "JWT",
      cty: "JWT",
    })
    .encrypt(secretKey);

  return token;
}
```

<Info>
  Tesouro provides `TESOURO_CLIENT_ID`, `TESOURO_CLIENT_SECRET`, and `TESOURO_WIDGET_SECRET`
  (exactly 32 bytes) during onboarding.
</Info>

<Info>
  **`organization_reference` is required.** Pass your opaque, stable identifier for the business or
  organization the applicant belongs to. Tesouro uses it to group applications under that
  organization — including before the applicant has a Tesouro user — so the widget can resolve the
  correct application status at load time. Resolve this value on your backend; never accept it from
  the browser.
</Info>
