Skip to content

Single sign-on across clients

This guide lets a user who signed in to one of your apps go straight into your other apps, without entering their password again.

That’s single sign-on, and there’s nothing to turn on. When a user signs in, the auth server starts a session for their browser. Any client that sends that browser to sign in afterwards finds the session, and the user goes straight through.

  1. Register each app as a client of its own, as Add sign-in to a web app or Add sign-in to a SPA or mobile app shows. Each gets its own client identifier, redirect URIs, consent setting, ACR level and tokens.

  2. Send every app’s users to the same auth server address, such as https://auth.example.com. The session cookie belongs to that host name. Your apps can run on any domains, but an auth server reached under two names holds two separate sessions.

  3. Sign in to the first app. The auth server asks for the password and starts a session.

  4. In the same browser, open the second app and start its sign-in, as an ordinary authorization request. The auth server finds the session and sends the browser straight back with a code, without showing a page. The second app redeems the code for tokens of its own.

  5. To sign the user in to the second app without even a click, send a silent request from it when its first page loads. Add prompt=none and redirect the whole page, rather than loading it in a hidden iframe: the session cookie is SameSite=Lax, so a request from a frame on another site never carries it, and the auth server’s pages refuse to be framed:

    GET /auth/authorize?client_id=second-app&redirect_uri=https%3A%2F%2Fsecond.example.com%2Fcallback&response_type=code&scope=openid%20profile&prompt=none&code_challenge=...&code_challenge_method=S256&state=... HTTP/1.1
    Host: auth.example.com

    A code means the user is signed in. login_required means there’s no session to use, so show your sign-in button. interaction_required and consent_required mean the user has a page to see: send them through the same request without prompt=none. access_denied means the user holds none of the scopes asked for, or has been disabled, which a sign-in doesn’t change. See prompt.

A session saves the user their password, not every step. A sign-in through it still shows a page when:

  • The second app needs a stronger sign-in. When its ACR level is higher than the session reached, the user enters only what the new level adds, usually a one-time code. That’s a step-up, and the session keeps the higher level afterwards.
  • The second app asks for consent. With Ask users for consent on and no earlier approval from the user covering every scope, or whenever it asks for offline_access, the user sees the consent screen. Consent is given to each client on its own, so approving the first app doesn’t approve the second. See Clients.
  • The request asks for a password. prompt=login, a max_age shorter than the time since the user last entered a credential (auth_time), or an id_token_hint naming another user each ask for the password whatever the session, and for a one-time code whenever the level needs one. See prompt and Sessions.
  • The session can’t be used. It has been idle too long or reached its maximum lifetime, so the user signs in again. See How long a session lasts. A user who has been disabled can’t use a session: disabling them ends their sessions, so their next sign-in answers “Your user account is disabled.” once the right password is entered, and a silent request gets login_required.

One session serves every client, so its lifetimes apply to all of them. They’re under Admin, Sessions in the admin console: by default a session ends after 2 hours idle, and after 24 hours whatever happens. A sign-in to any app through the session counts as activity, and so does a refresh with a normal refresh token from it.

Each app’s tokens keep their own lifetimes, set under Admin, Tokens, or on the client’s Tokens tab. See How long tokens last.

When the user signs out of one app with its ID token as the hint, the auth server removes only that client from the session. The other apps the user signed in to stay signed in, and their normal refresh tokens keep working. The browser’s session cookie is cleared, though, so the next sign-in in that browser asks for the password, for every app. See Sign users out.

The auth server doesn’t tell your other apps about a sign-out. Each one keeps its own session until it ends it, or until it next asks the auth server for something the ended session no longer gives, such as a refresh.

To end the session for every app at once, the user signs out with no hint and confirms on the sign-out page. Every app’s normal refresh tokens from the session stop working; offline ones keep working. Ending the session under Account, Sessions in the admin console goes further and revokes them all, offline ones included. See Ending sessions.