← All flows

OpenID Connect Flow

For signing users in and learning who they are

Who's involved
User, app, identity provider
Best for
Signing users in
Difficulty
Moderate

1Overview

How it works

OAuth 2.0 is about permission to use an API. OpenID Connect (OIDC) is a thin layer on top that answers a different question: who just signed in?

You use the Authorization Code flow with the openid scope. Along with the access token you receive an ID token: a signed JWT (JSON Web Token) containing facts about the user.

2Use cases

When would you use this?

Sign in with…

A 'Continue with Google' button on a note-taking site tells the site who you are.

Single sign-on at work

One company login opens the email, chat and HR tools without separate passwords.

Showing a profile

An app shows your name and photo straight after login, taken from the ID token.

Not the right fit? Only calling an API? Plain OAuth is enough. OIDC is for when the app needs to know who the user is.

3Interactive

Try it step by step

See it step by step

πŸ§‘
You
πŸ–₯️
App server
πŸ”
Login server
Sign in (openid)

Step 1 of 6

The app sends you to sign in

The app asks the login server to identify you by adding the openid scope to its request.

4Practical advice

Do this, avoid that

Good practices

  • Always validate the ID token β€” Check the signature, iss, aud, exp and nonce before trusting it
  • Send and check a nonce β€” It stops an old ID token being replayed
  • Use the sub claim as the user ID β€” Emails and names can change, sub does not
  • Use a certified library β€” Rolling your own JWT checks is easy to get wrong

Common mistakes

  • Treating an access token as proof of login β€” Only the ID token is meant to identify the user
  • Skipping signature checks β€” Anyone could forge a token
  • Using email as the unique ID β€” It can change or be reused
  • Sending the ID token to APIs β€” Use the access token for that

5Code

Code example

Show the code
OpenID Connect example
javascript
// OpenID Connect sign-in (server side)
// 1. Redirect the user to the provider
const url = new URL('https://auth.provider.com/authorize');
url.searchParams.set('response_type', 'code');
url.searchParams.set('client_id', CLIENT_ID);
url.searchParams.set('redirect_uri', REDIRECT_URI);
url.searchParams.set('scope', 'openid profile email');
url.searchParams.set('state', state);
url.searchParams.set('nonce', nonce);

// 2. In the callback, swap the code for tokens
const tokens = await fetch('https://auth.provider.com/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    code, redirect_uri: REDIRECT_URI,
    client_id: CLIENT_ID, client_secret: CLIENT_SECRET,
  }),
}).then((r) => r.json());

// 3. Validate the ID token (jose library)
const { payload } = await jwtVerify(tokens.id_token, jwks, {
  issuer: 'https://auth.provider.com',
  audience: CLIENT_ID,
});
if (payload.nonce !== savedNonce) throw new Error('Nonce mismatch');
// payload.sub is the user\'s stable ID