Available

Manage sessions, sign-out and MFA

Keep a person signed in with refresh tokens, sign them out, and require a fresh sign-in or multi-factor authentication (MFA).

When a person signs in, your project keeps a session for them. Your application never sees the session directly. It sees the tokens issued from it, and each of them names the session in the sid claim.

Use this guide to control that session from your application: keep the person signed in, sign them out, and require a fresh sign-in or multi-factor authentication (MFA) before a sensitive action.

Before you begin

You need:

  • An application that signs people in with the authorization code flow. To build one, follow Add sign-in to an app.
  • The application's client ID, and its client secret if it has one.
  • Your project's issuer, the project's URL. This guide uses https://acme.authfi.io.

Keep a person signed in

An access token lasts 15 minutes by default. To keep a person signed in for longer, exchange the refresh token for new tokens when the access token expires.

AuthFI issues a refresh token to every application that holds a client secret. An application without a client secret receives one when the project allows it, which it does by default. The setting is Refresh tokens for browser apps, in Settings > Sessions in the console.

POST

/oauth/token
curl https://acme.authfi.io/oauth/token \
  -d grant_type=refresh_token \
  -d refresh_token="$REFRESH_TOKEN" \
  -d client_id="$CLIENT_ID"

$REFRESH_TOKEN is the most recent refresh_token your application received. $CLIENT_ID is the application's client ID. An application with a client secret also sends it, as HTTP Basic authentication or as client_secret in the body.

ParameterValue
grant_typerefresh_token
refresh_tokenThe most recent refresh token
scopeOptional. A subset of the scopes first granted.

AuthFI returns new tokens, including a new refresh token:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Ii4uLiJ9...",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "...",
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ii4uLiJ9...",
  "scope": "openid profile email"
}

Follow these rules when you refresh:

  • Use each refresh token once. Every use returns a new refresh token. Store the new one and discard the old one.
  • Ask for the same scopes, or fewer. scope on a refresh can be a subset of what was first granted, and never more.
  • Read the sign-in claims as those of the original sign-in. sid, amr and auth_time in the new tokens are unchanged. Refreshing does not make a sign-in look newer.
A reused refresh token ends the refresh chain

If a refresh token that was already used is presented again, AuthFI revokes every refresh token descended from the same sign-in and answers invalid_grant. Access tokens already issued expire at the end of their lifetime, 15 minutes by default. This is what stops a stolen refresh token.

Sign a person out

To sign a person out, send the browser to the project's end-session endpoint.

GET

/oauth/logout
https://acme.authfi.io/oauth/logout?client_id=CLIENT_ID&post_logout_redirect_uri=https%3A%2F%2Fapp.acme.com%2F&state=abc
ParameterMeaning
client_idYour application. Needed for the person to be sent back.
post_logout_redirect_uriWhere to send the person afterwards
stateReturned to you unchanged

AuthFI ends the session and removes its cookie. What the person sees next depends on the request:

  • When client_id is given and post_logout_redirect_uri has the same scheme and host as one of the application's registered redirect URIs, the person is sent back to it.
  • Otherwise, the person sees a page that says they are signed out.

Signing out ends the session. To also stop the refresh token your application holds, revoke it:

POST

/oauth/token/revoke
curl https://acme.authfi.io/oauth/token/revoke \
  -d token="$REFRESH_TOKEN" \
  -d client_id="$CLIENT_ID"

The response is 200 with an empty body. See Revoke a token.

A session also ends without your application, when it reaches its total length or its idle time, or when the person's account is removed. See Session and token lifetimes.

End a session from the console

An administrator can end sessions in two places:

  • One or more sessions. Open Security > Sessions, select the rows, and choose End session. Confirm with the End session button, which names how many sessions it ends.
  • Every session of one person. Open Identity > Users, open the person's page, and choose Sign out everywhere. The person can sign in again.

Require a fresh sign-in

To make a person sign in again, add one of these parameters to the authorization request.

ParameterEffect
prompt=loginThe person signs in again, even with a session
max_age=600The person signs in again if they last did so more than 600 seconds ago. 0 means now.
login_hintFills in the account name on the sign-in page

For example, this request makes the person sign in again and fills in their account name:

echo "https://acme.authfi.io/oauth/authorize?response_type=code&client_id=$CLIENT_ID&redirect_uri=https%3A%2F%2Fapp.acme.com%2Fcallback&scope=openid%20profile%20email&state=$STATE&code_challenge=$CHALLENGE&code_challenge_method=S256&prompt=login&login_hint=ada%40acme.com"

$STATE and $CHALLENGE are created as in Add sign-in to an app. The browser returns to your redirect URI with a code, as for any sign-in.

The auth_time claim in the ID token tells you when the person last signed in. When you depend on max_age, check auth_time yourself.

Require MFA

To require multi-factor authentication (MFA) or a passkey, add acr_values to the authorization request. acr stands for authentication context class reference: a name for how strongly the person signed in.

echo "https://acme.authfi.io/oauth/authorize?response_type=code&client_id=$CLIENT_ID&redirect_uri=https%3A%2F%2Fapp.acme.com%2Fcallback&scope=openid%20profile%20email&state=$STATE&code_challenge=$CHALLENGE&code_challenge_method=S256&acr_values=urn%3Aauthfi%3Aacr%3Amfa"
acr_valuesThe person must have
urn:authfi:acr:1faSigned in
urn:authfi:acr:mfaCompleted MFA
urn:authfi:acr:passkeySigned in with a passkey
urn:authfi:acr:passkey_mfaUsed a passkey and completed MFA

Several values, separated by spaces, mean "any of these, or stronger".

If the level you asked for cannot be reached, the browser returns to your redirect URI with error=unmet_authentication_requirements.

Check acr yourself

Asking for a level is not the same as getting it. Before a sensitive action, read acr from the token and deny the action if the level is weaker than you need.

Require MFA from the console

acr_values requires MFA for one sign-in to one application. An administrator can also require it in the console, where MFA is labelled Second factor:

  • For everyone in the project. Open Settings > Sign-in. Under What they have to prove, set Second factor to Required (at every sign-in). The other choice is Optional (only under risk or a rule). Then choose Review and apply, and apply the change.
  • For one person. Open Identity > Users, open the person's page, and choose Require MFA in the More actions menu. The person enrols a factor at their next sign-in.

Check that it works

Send a person through an authorization request with prompt=login and acr_values=urn:authfi:acr:mfa, then exchange the code for tokens. In the new tokens:

  • auth_time is the time of the sign-in you just completed.
  • acr is urn:authfi:acr:mfa or a stronger level.
  • amr lists the methods the person used.

Then exchange the refresh token. The response contains a new refresh_token, and auth_time is unchanged.

Session and token lifetimes

A session has two limits. Whichever is reached first ends it.

LimitDefaultEnds the session when
Total length24 hoursThe time has passed, however active the person
Idle time8 hoursThe session was not used in that time

Tokens have their own lifetimes.

TokenDefault lifetime
Access token15 minutes
ID tokenThe same as the access token
Refresh token30 days

Change the lifetimes

  1. In the console, open Settings > Sessions.
  2. Under How long they stay signed in, enter a value in seconds.
  3. Choose Review and apply, and apply the changes.
FieldSets
Access tokenThe lifetime of an access token
Refresh tokenThe lifetime of a refresh token
SessionThe total length of a session
Idle timeoutThe idle time of a session

A shorter lifetime applies to what is issued next. It does not revoke anything already issued.

Sign-in strength claims

The tokens record what happened at sign-in.

ClaimMeaning
acrThe level reached, one of the acr_values in Require MFA
amrHow the person signed in: a list of the methods below
amr valueMethod
pwdA password
otpA one-time code
mfaMore than one method
hwkA key held in hardware, such as a passkey
userThe passkey verified the person (a PIN or biometric)
fedSigned in through another identity provider

Sign-in protections

These protections apply on the sign-in page of every project. Your application does nothing to turn them on.

ProtectionApplies whenResult
Lockout5 failed attempts in 30 minutes, for one account from one IP addressFurther attempts are rejected
Breached passwordsThe password is found in a known breachThe password is rejected, and the person is asked to change it
Expired passwordsThe password is past its age limitThe person must change it before sign-in completes
Rate limitMore than 300 requests a minute from one IP address, across all endpoints429

A breached password is rejected even when it is the correct one.

To change the lockout numbers, open Settings > Sign-in in the console. Under Failed sign-ins, Lock out after is the number of failed attempts, and Lockout lasts is the time in seconds. To change when a password expires, open Settings > Passwords and set Expires after, in days. In both places, choose Review and apply, and apply the changes.

Troubleshooting

What you seeCauseFix
invalid_grant on a refreshA refresh token that was already used was presented againStore the new refresh token from every response, and send the person to sign in again
invalid_scope on a refreshThe refresh asked for more than was first grantedSend the scopes first granted, or a subset
A signed-out page instead of a return to your applicationclient_id is missing, or post_logout_redirect_uri does not match a registered redirect URISend client_id, and a URI with the scheme and host of a registered redirect URI
The browser returns with error=unmet_authentication_requirementsThe acr_values you asked for could not be reachedHave the person enrol in MFA, or ask for a lower level
429More than 300 requests a minute came from one IP addressSend fewer requests

Every error is listed in the endpoint reference.

Next steps