Features
This page covers what a client can do, how people sign in to it and out of it, and what happens to their accounts over time.
Clients
Section titled “Clients”Registering a client
Section titled “Registering a client”| How | Who | Approved |
|---|---|---|
| Added | A manager, in /manage, which shows the secret once. |
Yes |
| Handshake | A person completes /handshake, and masks returns the credentials to the app server to server. |
Yes |
| Self-registered | The app, over dynamic registration, within the tenant’s ceiling. | No |
| Metadata document | The app, by using the URL of a document that describes it as its client_id. |
No |
Only an approved client may hold a masks: scope, sign in as itself, receive a
delegation, or name resources it serves, since a resource
trusts the tokens issued for it. A self-registered client that names resources is refused. A client grants required_scopes always and
allowed_scopes when asked. Archiving a client stops it signing anyone in and keeps its record.
A client named by its metadata document
Section titled “A client named by its metadata document”An app that has never registered can use the URL of a JSON document it publishes as its client_id,
as MCP clients do. masks fetches the document the first time the URL arrives, and registers the
client as self-registered.
{ "client_id": "https://app.example.com/oauth/client.json", "client_name": "Example App", "redirect_uris": ["https://app.example.com/callback"], "scope": "openid profile"}- The URL is https, has a path, and carries no fragment, credentials, or
.and..segments. - The document names its own URL as
client_id, is at most 5KB, and holds noclient_secret. masks fetches it under the same rules as every other address it calls, so a private address is refused. - The client authenticates with PKCE alone or with
private_key_jwt. - masks keeps the document for as long as its
Cache-Controlallows, between five minutes and a day, and for an hour when it gives nomax-age. Then masks fetches it again. - A document that cannot be fetched or is refused is not fetched again for five minutes, and the client cannot sign anyone in until it is accepted.
- A manager can archive the client to refuse it. Approving it keeps its redirect URIs, scopes, and keys, and later versions of the document change only its name, links, and logo.
Discovery sets client_id_metadata_document_supported to true while the tenant allows dynamic
registration. With registration off, masks fetches no documents.
Client authentication
Section titled “Client authentication”token_endpoint_auth_method |
|
|---|---|
client_secret_basic |
The secret in a Basic Authorization header. The default. |
client_secret_post |
The secret in the form body. |
private_key_jwt |
An assertion signed with the client’s own key, checked against its jwks or jwks_uri. |
none |
A public client, which proves itself with PKCE. |
masks stores only a hash of a secret, and refuses a client that authenticates by a method other than
the one it registered. A private_key_jwt assertion is accepted once and expires within an hour. An
unknown key makes masks fetch the jwks_uri again, so a client can rotate keys without telling it.
Consent
Section titled “Consent”masks asks each person once before a client receives anything. It asks again when the client asks
for more scopes or resources, passes prompt=consent, or sends
authorization details the person has not approved.
- Approving a handshake is the approver’s consent, and nobody else’s.
- A manager can turn
consent_requiredoff for an approved client, such as a first-party app. A self-registered client always asks. - A manager can set a client’s consent lifetime, from five minutes to 400 days. Once a consent is that old, the person is asked again. Without a lifetime, a consent lasts until it is revoked.
Subject identifiers
Section titled “Subject identifiers”A public client sees the person’s own identifier as sub. A pairwise client sees one derived for
its sector, the host of its redirect URIs, so two sectors cannot match their people. A client with
redirect URIs on more than one host registers a sector_identifier_uri that lists them all.
Signing in as itself
Section titled “Signing in as itself”A service that acts for no person, such as a nightly job, uses the client_credentials grant:
curl -u "$CLIENT_ID:$CLIENT_SECRET" https://acme.auth.example.com/token \ -d grant_type=client_credentials \ -d scope="xixo:catalog:read"A manager enables it for an approved client. The token names the client as sub, carries no
refresh token, and never holds a scope that acts for a person, such as openid or masks:manage.
Signing in
Section titled “Signing in”Sign-in policies
Section titled “Sign-in policies”A sign-in uses the organization’s policy, the client’s, the
tenant’s default, or the built-in one, in that order. /manage edits them.
| Setting | Default | |
|---|---|---|
first_factors |
password, passkey, provider |
How a person proves who they are. Also accepts email_code. |
second_factors |
otp, passkey, backup_codes |
Also accepts email, sms, and trusted_device. |
second_factor_required |
off | Ask for a second factor at every sign-in. |
password_minimum |
8 |
Minimum password length. |
refuse_common_passwords |
on | Refuse passwords on a list of about 47,000 common ones. |
providers |
every provider | Which providers to offer. |
signup |
off | Let people create their own accounts. |
confirmation |
none |
How a new account is confirmed: none, code, link, or approval. |
email_domains |
any | The address domains signup and provider sign-in accept. |
A policy also refuses breached passwords and steps up risky sign-ins. A factor the policy does not list is hidden and refused. The tenant’s default always keeps a password or a passkey, so managers are never locked out. Whatever the policy says, a manager must set up an authenticator app or a passkey as a second factor.
First factors
Section titled “First factors”| Factor | |
|---|---|
| Password | Stored as a bcrypt hash, and checked against the policy’s minimum and common passwords. |
| Passkey | WebAuthn. It counts as a second factor too when the authenticator verified the person. |
| Provider | An account at Google, Okta, a SAML identity provider, and others. See SSO / SAML. |
| Emailed code | A six-digit code sent to the account’s address, with no password. See signing in with an emailed code. |
When the policy offers passkeys and the browser supports passkey autofill, the sign-in field lists the person’s passkeys among its suggestions. Choosing one signs in without typing a nickname or address.
masks names each passkey after its authenticator from the FIDO Metadata Service, and marks one the service reports as compromised. A passkey whose signature counter fails to advance is treated as copied, and its sign-in is refused.
Signing in with an emailed code
Section titled “Signing in with an emailed code”A policy that offers Emailed code shows Email me a code after the person identifies, or only that button when it offers no password. Since the tenant’s default keeps a password or a passkey, offer a code alone on a policy for the apps that want it.
- The code has six digits, lasts ten minutes, and allows five attempts. Another can be sent after 30 seconds, within the account’s rate limit.
- An address or nickname with no account gets the same screen and no mail, so the form does not reveal who has an account. Neither does an account that has reached its limit of codes.
- An account that has not accepted its invitation gets no code, and neither does one with a password and an unconfirmed address. This stops someone from signing up with another person’s address, setting a password, and waiting for that person to confirm the address by signing in with a code.
- Signing in with a code confirms the address of an account with no password.
- Under a policy that hides who has an account, the address is proven first.
- The code proves the inbox, so it records
otpand never counts as a second factor. The email second factor is not offered after it, because both would come from the same inbox.
Second factors
Section titled “Second factors”| Factor | |
|---|---|
| Authenticator app | A TOTP code. Recorded as otp in amr. |
| Passkey | A registered passkey. |
| Backup codes | One-time codes, for when the other factors are lost. |
| Email code | A six-digit code sent to a confirmed address. Recorded as otp. |
| Text message code | A six-digit code sent through the tenant’s SMS adapter. Recorded as sms. |
| Trusted device | The person enters a code shown on the new sign-in into the account page of a device they trusted. |
Each person turns email and text message codes on from the account page once the address is confirmed. Once on, a code is asked for at every sign-in, under any policy, as an authenticator app is. Changing the address turns its codes off, and so does a password reset through an emailed link. An email code does not confirm a sign-in whose first factor was a provider.
After a second factor from an authenticator app, a passkey, or a sent code, the person can ask masks to trust the device. The second factor is then skipped on that device for 30 days, or until the device is signed out.
Device sign-in
Section titled “Device sign-in”A device that cannot run a browser, such as a television or a CLI, uses the device grant. It asks
/device_authorization for a code, shows the person the code and /device, and polls /token
until the person approves on another device.
| Poll response | Do |
|---|---|
authorization_pending |
Keep polling. |
slow_down |
Add five seconds to the interval. |
access_denied |
Stop. The person declined. |
expired_token |
Request a new code. Codes last fifteen minutes. |
A user code is eight letters with no vowels, such as BCDF-GHJK. Case and dashes are ignored.
Signing out
Section titled “Signing out”Signing out from an app
Section titled “Signing out from an app”An app sends the browser to /logout, the end_session_endpoint in discovery:
GET https://acme.auth.example.com/logout ?id_token_hint=eyJ… &post_logout_redirect_uri=https://app.example.com/signed-outpost_logout_redirect_uri must be one the client registered. A GET without an id_token_hint for
the signed-in person shows a confirmation screen first, so a link on another site cannot sign
someone out.
Telling other apps
Section titled “Telling other apps”When a session ends, masks posts a signed logout+jwt to the backchannel_logout_uri of every app
the person used in it. masks tries each delivery five times, and records one that still fails on the
person’s activity. masks never posts to a private address for a self-registered app.
Sessions and devices
Section titled “Sessions and devices”A session is one signed-in browser. A device is the browser itself, known by a cookie that lasts 400 days.
| Action | Ends |
|---|---|
| Revoke a session | That sign-in. |
| Sign a device out | Every session and token on it, and its remembered second factor. |
| Sign an actor out | Every session and refresh token on every device. |
| Block a device | Every request from it, before any password is checked. |
/manage also lists devices that never finished signing in, so they can still be blocked.
How long a session lasts
Section titled “How long a session lasts”A session lasts 14 days by default. A sign-in policy changes this under Sessions.
| Setting | Effect |
|---|---|
| Lifetime | A session started under the policy expires this long after signing in. An app covered by the policy asks for the password again in any older session. |
| Idle timeout | A session started under the policy ends after this long with no request, and masks records session.expired. An app covered by the policy asks for the password again after a gap this long. |
Both run from 5 minutes to 400 days, and the idle timeout is shorter than the lifetime. A refresh token issued in a session that either setting bounds stops working when the session ends, including when the person signs out. A session with neither setting leaves refresh tokens to their own lifetime.
Organizations
Section titled “Organizations”An organization is a customer inside a tenant, whose members each hold a role there. An app that
asks for the organization scope signs a person in as a member of one. See
organizations.
Accounts
Section titled “Accounts”Suspending and deleting
Section titled “Suspending and deleting”A manager suspends, restores, or deletes an actor in /manage, and an identity provider does the
same over SCIM. Suspending signs the actor out everywhere and
refuses every sign-in until a manager restores them. Deleting signs them out everywhere, tells apps
that registered for back-channel logout, and removes their passkeys, consents, connected accounts,
and photo. Nobody can suspend or delete the last manager.
Idle accounts
Section titled “Idle accounts”A tenant can suspend accounts nobody uses, and later delete them. Under Idle accounts in
/manage, Suspend and Delete each take a period, and either can be left at Never. When
both are set, deleting comes later than suspending.
- Signing in, and an app refreshing a token for the person, both count as using the account.
- masks emails a warning 30 days before each step, and takes the step no sooner than 30 days after its warning. Turning the settings on or changing them gives every account a fresh 30 days.
- Signing in before a suspension keeps the account. Once suspended, only a manager can restore it, and the warning before deletion says so.
- With Suspend set, only accounts suspended for being idle are deleted. With Suspend at Never, an idle account is deleted directly. An account a manager or an identity provider suspended stays until they decide.
- An account with no confirmed email address cannot be warned, and follows the same schedule.
- The last manager, accounts provisioned over SCIM, and invitations nobody has accepted are never touched.
- A daily job does the work, and records each warning, suspension, and deletion as an event.
Downloading your own account
Section titled “Downloading your own account”A person downloads everything masks holds about them as a JSON file from Download your account on their account page.
The file holds their profile, which factors they use and when each was added, their passkeys, organizations and roles, connected accounts and what each app was allowed to do with them, the apps they consented to, their devices, and every event about them that the tenant still keeps. It holds no password hash, authenticator secret, backup code, token, or key.
Each download is recorded as account.exported, and the person is emailed unless they turned off
the Account group under Emails we send you. A person can download their account five times an hour.
Deleting your own account
Section titled “Deleting your own account”A person deletes their own account at the bottom of their account page, by typing its nickname or email. The last manager and an account provisioned over SCIM cannot delete themselves.
Downloading and deleting both ask a person who signed in more than 15 minutes ago to sign in again first.