Authentication levels and claims in your tenant
Decide how strong a sign-in an application can ask for, and what each application learns about the people who sign in.
The same controls apply to BTL's own organization under BTL Admin, OAuth Management.
Authentication levels
Open OAuth Management, Flow Policy and add levels under Authentication levels. A level lists the sign-in methods it requires: a password, an email sign-in link or another one-time code, a one-time code (from an authenticator app, an email code or a recovery code), a passkey, or two different kinds of factor. Because an email sign-in link records the registered value otp, a level that asks for an email link is met by any one-time code. It can also require a sign-in within a number of seconds. Its acr value is named for its methods in a fixed order, such as urn:btl:acr:pwd or urn:btl:acr:pwd+otp, so the value says exactly what it proves. A level can also require identity proofing at an assurance level, which is added to its value, such as urn:btl:acr:pwd+ial2; see Identity proofing in your tenant. The label is shown to people on the stronger sign-in page. A tenant can define 10 levels, each with a different value.
List levels from weakest to strongest. An ID token names the level the sign-in met in acr: the first requested level it meets, or without a request the strongest level it meets. When no level is met, acr is left out. Discovery lists the levels in acr_values_supported.
Asking for a level
An application asks for levels with acr_values, up to 10 values separated by spaces in order of preference, or with acr in the claims parameter. A client can also have Default authentication levels, used when its request names none. Values the tenant does not define are ignored. By default a request is voluntary unless the claims parameter marks acr essential: a voluntary request that cannot be met continues, and acr names what the sign-in did meet, while an essential one ends with unmet_authentication_requirements.
GET /oauth/authorize?client_id=...&response_type=code&scope=openid
&acr_values=urn:btl:acr:pwd%2Botp&...In a URL, write the + in a value as %2B; a plain + means a space, which would ask for two values.
Flow Policy decides what a requested acr_values means, under When a request names acr_values in Authentication levels. As a preference (the default), someone who cannot meet a requested level still signs in, and acr names the level they did meet. As a requirement, someone who can meet a requested level with a stronger sign-in is asked for it, and otherwise the application receives unmet_authentication_requirements at its redirect URI; a request that names only values the tenant does not define is refused before sign-in. Protocol Audit records these refusals as acr_values_unmet and acr_values_unknown. The choice applies to authorization requests: a CIBA backchannel request's acr_values stay a preference, matched against how the approving browser was signed in. An essential acr in the claims parameter is always a requirement, and a client's default levels stay a preference. Discovery shows the choice as btl_acr_values_required while levels exist.
Stronger sign-in
When the signed-in user does not meet a requested level, the tenant asks for what is missing. A missing one-time code or passkey is asked for on a Confirm it is you page that offers only the methods the user has set up that provide it, and the user returns to the request with those methods added to the sign-in. A missing password or email sign-in, or a sign-in older than the level allows, asks the user to sign in again. With prompt=none nothing is shown: the application receives interaction_required or login_required. Each stronger sign-in is recorded in Audit as step_up_required and step_up_completed.
Changes that need a level
Under Changes that need a level, choose a level for changing a password, adding or removing sign-in methods and recovery codes, changing an email address, or making any change in tenant management from the tenant's own host. A user below the level is refused, sees a Confirm with a stronger sign-in link on the page, and can try again once they meet it. Refusals are recorded in Audit as step_up_required. A person who cannot reach the level for sign-in method changes at all, such as someone with no passkey when the level needs one, may still add a method the level needs after a recent sign-in, so they can reach it; every other method change waits until they meet the level.
Authentication context in access tokens
Turn on Authentication context in access tokens for an access token manager in Access Token Management to add auth_time, amr and acr to its tokens and to introspection answers. An API that validates JWTs locally can then require a recent or stronger sign-in, as the sample photo API does. It is off by default, since it tells every API how the user signs in. Claim mappings cannot set these names.
The claims parameter
An application can ask for individual claims with the claims parameter (OpenID Connect Core section 5.5), a JSON object with id_token and userinfo members:
{"id_token": {"email": null, "acr": {"essential": true, "values": ["urn:btl:acr:pwd+otp"]}},
"userinfo": {"https://example.com/claims/department": null}}A requested claim is sent to that place even when its mapping normally goes only to the other one. A claim asked for without its scope, such as email with only scope=openid, is released once the person approves it: the consent page lists it under Individual details, remembered consent never covers it, and with prompt=none the request ends with consent_required. A client set to skip consent receives it without the page. A scope the person unticks on a per-scope consent page withholds the claims asked for under it, and so does a refresh request that leaves the scope out. A claim the tenant does not issue is simply left out. A sub with a value gets a positive answer only for that user; anyone else ends with login_required. Turn the parameter off under Claims parameter in Flow Policy, and it is ignored and discovery says claims_parameter_supported: false.
Claim sources
Claim mappings in the token managers can copy a user's groups, their directory roles (their own and those of every group they belong to), the sign-in methods and authentication level, when the user's profile last changed (for the standard updated_at claim, in seconds since 1970; a change to names, email, password, groups or lock state moves it, a sign-in does not), and any custom SCIM extension attribute, written as extension:<schema URN>:<attribute name>. A list attribute can keep only the items that start with a filter, such as groups named app-. Claim names can be short names or collision-resistant URIs such as https://example.com/claims/department or urn:example:department (RFC 7519 section 4.2).
Directory roles are managed on the Groups page. Create a role with a short ID, such as editor, which is the value tokens carry. Choose Directory roles on a group to give its members roles. Directory roles grant nothing in tenant management. A group can separately carry management roles; changing that group's membership then needs authority to assign those roles. SCIM cannot change membership of a group that carries management authority. A directory role that users or groups still hold cannot be deleted, and the built-in member role stays.
Pairwise subjects
A client's Subject identifiers setting is public by default: the same user has the same sub for every client. Choose Pairwise and the client receives a sub computed for its sector, so clients of different sectors cannot recognize the same person (OpenID Connect Core section 8.1). Clients with the same sector share pairwise values. Without a sector entered, the one host all of the client's redirect URIs share is the sector. A pairwise sub replaces any sub setting of the client's ID token and access token managers: its ID tokens, its JWT access tokens and introspection of its tokens carry the same value, and UserInfo returns it too. Another value a token manager adds from the user record, such as an email claim, can still identify the person, so leave such claims out of a pairwise client's tokens. Changing a client's subject setting or sector ends its existing tokens, because its users' sub changes.
Client options
In Clients, the OpenID Connect settings also include Default max_age, which asks for a new sign-in when a request sends no max_age and the sign-in is older, and Application type. A native client may register private-use URI scheme redirects such as com.example.app:/callback (RFC 8252 section 7.1): the scheme must be a reverse domain name with at least one dot, and http, https, javascript, data and file are refused. Every ID token includes auth_time, so there is no setting to require it.
Under App launcher, enter the application's https Login initiation URI and choose List on people's Applications page. An enabled, approved application then appears on the tenant user's Applications page. Opening it sends iss and, when configured, target_link_uri and login_hint to the application. The application starts its own sign-in request; the launcher does not send a code or token.
Consent choices and links
Set Consent choices to A choice for each scope and the consent page lists each scope with a checkbox. People can leave out any scope except openid, and the client receives only the scopes they allowed; remembered consent covers what was allowed. The client's logo, home page, privacy policy and terms of service appear on the consent page. They must be https links, open in a new tab and do not tell the linked site where the person came from.
Registration from a request
An authorization request with prompt=create opens the tenant's registration page, and the new user returns to the request once registered. It needs self-registration, turned on under Authentication, and discovery lists create in prompt_values_supported only then. Otherwise the request ends with invalid_request.
Permissions, history and limits
Levels, changes that need a level and the claims parameter are part of Flow Policy and need its update permission. Client settings need the client permissions, and directory roles need tenant.directory_roles.manage, which Tenant Admin holds. Every change is recorded in Audit. A tenant can define 50 directory roles, shown in its usage view.