Skip to content
TDACorpIDENTITYSign in

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.

ScopeWhat it asks for
openidConfirms 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.
profileDisplay name, username, profile picture (only when the person has set one), language, time zone and when the profile last changed.
emailEmail address and whether it is verified.
offline_accessA refresh token, so your application can keep working without asking again.
tdaid.tenantNames 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 eidosA 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.

  1. 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.
  2. Each scope is listed with a plain-language title. openid is 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.
  3. What the person ticks becomes the grant. The authorization code, and therefore the tokens, carry only those scopes, plus openid.
  4. 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 resource must 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 aud stays your client id. The access token is not accepted at identity's /userinfo, because it is addressed to someone else.
  • client_credentials and token exchange refuse resource. A service token is issued to the calling client, and an exchange names its target with audience: 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.

Verified against commit eb6dd3b