Scopes and consent
How an application asks for scopes and a resource, what the person sees on the consent screen, and why tokens carry no roles claim.
On this page
A scope is a request for a category of access. An application asks for scopes when it starts a sign-in, the person sees them on a consent screen (for a third-party application), and the tokens carry what was granted. This page also covers the resource indicator that decides which API a token is for.
Ask for scopes#
Pass scope to createAuthRoutes or createNextAuthHandlers, or to rp.startAuthorization. It takes a space-separated string or an array. openid is required; a request without it is refused. Every scope must be one the client is registered to hold, or the request is refused with invalid_scope.
| Scope | What it asks for |
|---|---|
openid | Confirms the person is signed in and gives your application the account identifier (sub; for a client registered pairwise, an identifier specific to that client). On its own it shares no name and no email address. |
profile | Display name, username, profile picture (only when the person has set one), language, time zone and when the profile last changed. |
email | Email address and whether it is verified. |
offline_access | A refresh token, so your application can keep working without asking again. |
tdaid.tenant | Names the organization the person is signing in to, on the consent screen. It does not add claims: tid and tslug come from the sign-in itself. |
<ns>, for example eidos | A product scope: access to that product, limited to what the person's role there allows. |
The full list is generated from the scope table: Scopes. If you pass no scope, the route helpers ask for openid profile email.
import { createAuthRoutes } from '@tdacorp/identity-sdk/rp'import { rp } from './relying-party'export const routes = createAuthRoutes({ rp, appOrigin: 'https://app.example.com', cookieSecret: process.env.APP_COOKIE_SECRET ?? '', // openid is required. offline_access asks for a refresh token. A bare namespace such as eidos is a product scope. scope: ['openid', 'profile', 'email', 'offline_access'], // The resource server the access token is for. It becomes the token's aud. authorization: { resource: 'https://api.example.com' }, onLogin: (login) => new Response(null, { status: 303, headers: { location: login.redirectTo } }),})// A refresh may narrow the scope. It can never widen it.export const refreshNarrower = (refreshToken: string) => rp.refresh(refreshToken, { scope: 'openid' })A refresh may ask for fewer scopes than the family holds and never more. Read tokens.scope after sign-in to see what you were actually given, because the person can grant fewer than you asked for.
What the person sees#
Consent is for third-party clients only. A first-party client, one that TDACorp ships, skips the screen.
- If the person has already granted every scope you ask for, identity skips the screen. If you ask for a scope they have not granted, it asks again.
- Each scope is listed with a plain-language title.
openidis fixed. The others are checkboxes that start ticked, and the person can untick them. A scope identity does not recognise starts unticked and is described as a permission the platform does not recognise. - What the person ticks becomes the grant. The authorization code, and therefore the tokens, carry only those scopes, plus
openid. - The grant is stored. It can be removed at any time from Connected apps in the person's account. Removing it revokes your refresh tokens for that person, and a client with a back-channel logout address is told.
You can force the screen with prompt: ['consent']. With prompt: ['none'] and no covering grant, the request fails with consent_required instead of showing anything. Pass either in the authorization option.
Ask for a resource#
RFC 8707 resource indicators decide which API an access token is addressed to. Without one, the access token's aud is your own client id. With one, aud is the resource.
- A resource is an address registered for a product. Matching is exact string equality.
- At the pushed authorization request each
resourcemust be registered, and your client must be first-party or belong to that product. A resource that is unregistered and one that is registered but not allowed to you get the same answer,invalid_target, so the registry cannot be enumerated. A request may name at most eight. - Once the organization is known, at the authorization request, the product that owns each resource must also be installed in that organization. If it is not, the answer is the same
invalid_target. The check is repeated when the code is exchanged and when a refresh token is used, so suspending the installation between those steps stops the token being issued. - At the token endpoint a request names at most one resource, and it must be one of those authorized. If you name none and exactly one was authorized, that one is used. If several were authorized and you name none, the answer is
invalid_target. - Every access token has exactly one
aud. For several resources you make several token requests. - The ID token's
audstays your client id. The access token is not accepted at identity's/userinfo, because it is addressed to someone else. client_credentialsand token exchange refuseresource. A service token is issued to the calling client, and an exchange names its target withaudience: see Service to service.
In the SDK, set authorization: { resource } on the route helpers, or resource on startAuthorization. It is sent at the pushed authorization request and again at the code exchange. rp.refresh(token, { resource }) selects one on a refresh. The receiving API verifies the token with createResourceServer, passing the resource address as its audience: see Protect your API.
There is no roles claim#
Tokens carry no roles claim and identity has no roles scope. A scope says which application a token may be presented to. It does not say what the person may do there. Permissions come from a decision API that identity answers for each question, not from the token. See Protect your API for the status of that API in the SDK.
The claims identity advertises are sub, name, email, email_verified, locale, zoneinfo, preferred_username, picture, updated_at, tid, tslug, acr, amr, auth_time and sid. The generated list is in OIDC endpoints.
Related#
- Consent screens: the screen itself, and how to build for every answer.
- Resource indicators: the full rules for
resource. - Authorization code with PKCE: where the scopes go in the request.