How sign-in works
Follow a sign-in from your app through Access and an Identity Orchestration journey and back. Learn which endpoints your app calls, what the session and tokens are, and how logout works.
The parts and who talks to whom
Your app never signs users in itself. It sends them to Access, the tenant's OpenID Connect (OIDC) and OAuth 2.0 provider. Access decides how the user authenticates, runs that authentication, and gives your app tokens.
| Part | Role in a sign-in |
|---|---|
| Your app | The relying party. It is registered in Access as a web client and only talks standard OIDC. |
| Access | The OIDC provider. It owns the authorize, token, logout and discovery endpoints, the single sign-on session and the tokens. |
| Identity Orchestration (IO) journey | Optional. The pages and logic of the sign-in: password, passkey, OTP, consent, profile completion. Access hands the user to the journey and gets the result back. |
| Identity Broker | Optional. Connects external identity providers such as Google, Microsoft Entra ID, SAML IdPs and national eIDs. See External identity providers. |
| Identity Store | Holds the user. Access and the journey read and update the user there. |
Because the app only speaks OIDC, you can change the whole sign-in experience in IO or Access without touching the app.
Where Identity Orchestration journeys fit
Access does not have to draw the sign-in pages itself. When the identity provider behind a web client is an IO journey, Access hands the user over to the journey and waits for the result. The app does not notice: it still talks only to Access.
- 1
Access starts the journey
Access redirects the browser to the journey with a short-lived signed
context_token. It carries the transaction, the requesting client, and the request context (scopes,acr_values,login_hint, locale). It expires after about 60 seconds, so the journey must read it right away. - 2
The journey runs
It starts with the Start (Access) node, which checks the token. Then it shows its pages and calls the platform services: password, passkey, OTP, consent.
- 3
The journey hands the user back
The End Success (Access) node reports the authenticated user to Access from the backend, with the authentication methods used and the assurance level reached.
- 4
Access finishes the OIDC flow
Access sets its session and redirects the browser to the app's
redirect_uriwith the authorization code.
Which journey runs depends on the web client's Identity provider setting and on the assurance level the app asks for with acr_values (or the client's Default ACRs). An app that needs a stronger sign-in, for example before a payment, asks for a higher level. If the current session doesn't meet it, Access sends the user through a step-up.
A journey that someone opens directly, from a bookmark for example, has no context_token. It cannot sign anyone in. Show a page that tells the user to start again from the app.
To build the journey itself, start with Getting started with Identity Orchestration.
Sessions
There are two sessions. Keep them apart in your head.
| Session | Owned by | What it does |
|---|---|---|
| Access session | Access (a cookie on the tenant host) | Single sign-on. While it is valid, a new authorization request from any app signs the user in without asking again, unless step-up is needed. |
| App session | Your app | Your own cookie or token store. Access doesn't see it or end it, except through front-channel logout. |
Set the Access session lifetime in the Access admin console, under the system settings for sessions:
- Inactivity timeout: each interaction extends the session by this amount.
- Maximum session length: after this, the user signs in again, whatever their activity.
- Changes apply to new sessions and to sessions updated after you save. Existing idle sessions keep their old limits.
On the web client you can also limit parallel sessions. Allow simultaneous sessions off means a new sign-in cleans up the user's older sessions. With it on, Maximum simultaneous sessions (2 to 25) invalidates the oldest session when the limit is passed.
Tokens
| Token | For | Format | Lifetime setting |
|---|---|---|---|
| ID token | Your app: who signed in, when and how (sub, aud, acr, profile claims from the granted scopes). | Signed JWT (RS256 by default). | ID token validity on the web client's OpenID Connect settings. |
| Access token | APIs your app calls on the user's behalf. Carries aud, scope, sub, client_id. | JWT or opaque, set per web client with Access token format. | Access token validity, in seconds. |
| Refresh token | Getting a new access token without sending the user back to Access. | Treat it as an opaque string. Store it like a password. | Refresh token validity, counted from the first access token. Empty means no expiry. |
- Validate JWTs locally: verify the signature with the keys from
/oauth/v1/keys, then checkiss,audandexp. Access rotates its signing keys, so look keys up bykid; don't pin one key. - Opaque access tokens can't be read by the API. The API asks Access through token introspection.
- Refresh tokens are issued only with the authorization code grant and only when Issue refresh token is on. Each refresh returns a new refresh token and invalidates the old one.
- Replay protection: with Revoke all active refresh tokens when an old refresh token is used on, reusing an old refresh token revokes the whole chain and the user must sign in again. Keep it on.
What ends up inside the tokens depends on scopes and claims mapping. See Scopes and claims.
Logout
Clearing your app's cookie is not enough: the Access session would sign the user straight back in. Send the browser to the end session endpoint.
GET https://{tenant-host}/oauth/v1/logout
?id_token_hint={id-token}
&post_logout_redirect_uri=https%3A%2F%2Fapp.example.com%2Fsigned-out
&state={random}- 1
Access ends its session
This signs the user out of every app that shares the Access session.
- 2
Front-channel logout
Access returns a page with one hidden iframe per app in the session that has a Front-Channel Logout URL. Each app clears its own session there.
- 3
Redirect
The browser goes to
post_logout_redirect_uri. It must be the client's Post Logout Redirect URL or one of its Additional Post Logout Redirect URLs. Without one in the request, Access uses the default, or stays on its logged-out page if none is set.
- Turn on Delete access/refresh tokens on logout for the web client, especially for public clients using PKCE. Otherwise tokens stay valid until they expire.
- To drop one token without ending the session, call
POST /oauth/v1/revokewithtokenandtoken_type_hint. - Users who signed in through an external identity provider are signed out there only if single logout is set up for that provider in the Identity Broker.
When sign-in fails
| Symptom | Likely cause | Fix |
|---|---|---|
| Access shows an error instead of redirecting | The redirect_uri doesn't exactly match a registered Redirect URL. | Register the exact URI, including path and trailing slash, on the web client. |
| Request refused because of the scope | The app asked for a scope that isn't in the client's Default or Additional scopes, or doesn't exist. | Add it to the client. See Scopes and claims. |
| Request fails with no scope sent | The client has no Default scopes. | Add openid (and what the app needs) to Default scopes. |
invalid_grant on the code exchange | The code expired, was used twice, or the code_verifier or redirect_uri differs from the authorize request. | Exchange the code at once, only once, with the same values. |
| The journey says to start from the application | It was opened without a context_token, or the token expired. | Always start from the app. Don't link to the journey URL. |
| ID token lacks a claim | The scope wasn't granted or the attribute has no value. | Check the scope's claims mapping and the user's data. |
For journey-side problems, see Troubleshooting Identity Orchestration.