mirror of
https://github.com/better-auth/better-auth.git
synced 2026-10-20 20:26:55 -05:00
docs(oauth-provider): show client-aware access token claims (#11656)
This commit is contained in:
@@ -735,7 +735,7 @@ Any other authenticated client gets `{ active: false }`, the same response as an
|
||||
|
||||
#### Which claims come back
|
||||
|
||||
Opaque and JWT access tokens return the same claims for the same grant: your `customAccessTokenClaims` and any per-resource `customClaims`. The server owns the reserved claim names (`iss`, `sub`, `aud`, `scope`, `auth_time`, and similar). If a callback returns one of those, it is dropped rather than allowed to overwrite the server's value.
|
||||
Opaque and JWT access tokens return the same claims for the same grant: extension `claims.accessToken` contributors, your `customAccessTokenClaims`, and any per-resource `customClaims`. The server owns the reserved claim names (`iss`, `sub`, `aud`, `scope`, `auth_time`, and similar). If a callback returns one of those, it is dropped rather than allowed to overwrite the server's value.
|
||||
|
||||
The two formats differ in one way. An opaque token is recomputed on each call, so introspection shows its current state: change a resource's claims, and the next introspection reflects the change. A JWT carries what was signed at issuance and never changes. Request a JWT (by passing a `resource`) when you want claims frozen at issuance; use an opaque token when you want the current state.
|
||||
|
||||
@@ -1398,6 +1398,34 @@ oauthProvider({
|
||||
})
|
||||
```
|
||||
|
||||
#### Client-Specific Access Token Claims
|
||||
|
||||
Use an `extensions[].claims.accessToken` contributor when a claim depends on the client a token is issued to. `customAccessTokenClaims` receives only the client's `metadata`, while a contributor receives the full `client` record, including its `clientId` and `referenceId`.
|
||||
|
||||
This example adds the client's organization to tokens for one API. It assumes [`clientReference`](#organizations) assigns each client to an organization, and that clients are linked to the API resource, for example with `resources` on [`adminCreateOAuthClient`](#resources):
|
||||
|
||||
```ts title="auth.ts"
|
||||
const apiResource = "https://api.example.com";
|
||||
|
||||
oauthProvider({
|
||||
resources: [apiResource],
|
||||
extensions: [{
|
||||
claims: {
|
||||
accessToken: ({ client, resources }) => {
|
||||
if (!resources?.includes(apiResource) || !client.referenceId) return {};
|
||||
return { "https://example.com/org": client.referenceId };
|
||||
},
|
||||
},
|
||||
}],
|
||||
})
|
||||
```
|
||||
|
||||
`client.referenceId` is the client's owner, set at registration by `clientReference` or by an [initial access token](#dynamic-client-registration) during protected dynamic registration. It is not the top-level `referenceId` that both callbacks also receive, which is the consent reference from `postLogin.consentReferenceId`.
|
||||
|
||||
Opaque-token introspection re-derives contributor claims without `grantType` or `sessionId`, so base any claim needed at introspection on the client, scopes, and resources. `customAccessTokenClaims` and per-resource `customClaims` override a contributor claim with the same name; see [Claim precedence](#claim-precedence).
|
||||
|
||||
An organization claim does not prove that the client is still connected. A JWT keeps its claims until it expires, so a resource server that must react to a disabled client right away should introspect the token, which reports tokens of disabled clients as inactive.
|
||||
|
||||
#### Custom Token Response Fields
|
||||
|
||||
Unlike the claim callbacks above (which add data *inside* JWT payloads), `customTokenResponseFields` adds fields to the **token endpoint JSON response** alongside `access_token`, `token_type`, etc. Standard OAuth fields cannot be overridden.
|
||||
|
||||
Reference in New Issue
Block a user