OAuth 2.0 & OIDC: The Protocols Behind Single Sign-On (SSO)

How OAuth 2.0 and OpenID Connect power enterprise Single Sign-On — tokens, grants, PKCE, and why every organisation needs a standards-based SSO strategy.

10 min read

SSO Starts Here

Every employee knows the feeling: one click on “Sign in with Google” or “Sign in with Okta” and you’re into Slack, Jira, and GitHub without typing a password again. That seamless experience is called Single Sign-On (SSO), and it runs on two protocols working together:

  • OAuth 2.0 handles authorisation — “This app can read your profile”
  • OpenID Connect (OIDC) handles authentication — “You are alice@corp.com”

OIDC is a thin identity layer built on top of OAuth 2.0, defined by the OpenID Foundation. Together, they are the standard backbone for enterprise SSO.

How SSO Works: The User’s Experience

sequenceDiagram
    participant User
    participant Slack
    participant Jira
    participant IdP as "Corp IdP (Okta / Azure AD)"

    User->>Slack: Open Slack
    Slack->>IdP: Redirect to /authorize
    IdP->>User: Enter credentials
    User->>IdP: Authenticate
    IdP->>IdP: Set SSO session cookie
    IdP->>Slack: Redirect back with auth code
    Slack->>IdP: Exchange code for tokens
    IdP->>Slack: id_token + access_token
    Slack->>User: Welcome (signed in)

    User->>Jira: Open Jira
    Jira->>IdP: Redirect to /authorize
    IdP->>IdP: SSO session still valid (cookie)
    IdP->>Jira: Redirect back (no password needed)
    Jira->>IdP: Exchange code for tokens
    IdP->>Jira: id_token + access_token
    Jira->>User: Welcome (auto signed in)

The user authenticates once with the corporate Identity Provider (IdP) — Okta, Azure AD, Keycloak, or any OIDC-compliant provider. The IdP sets a session cookie. Every subsequent app redirects to the IdP, which recognises the existing session and issues tokens without prompting for credentials again.

OAuth 2.0: Delegated Authorisation

Before OAuth, sharing access meant handing over your password — the “valet key” problem. OAuth solves this by letting a user grant an app limited, revocable access to specific resources, without sharing credentials. Defined in RFC 6749 and RFC 6750.

The Four Actors

Every OAuth SSO flow has the same four roles (RFC 6749 §1.1):

ActorIn SSO Terms
Resource OwnerThe employee (owns their identity data)
ClientThe app requesting SSO access (Slack, Jira, etc.)
Authorisation ServerThe corporate IdP (Okta, Azure AD, Keycloak)
Resource ServerThe API behind the app that validates tokens

Authorisation Code + PKCE Flow

The most common SSO grant type. PKCE (RFC 7636) binds the authorisation code to the client that started the flow, preventing interception attacks even without a client secret.

sequenceDiagram
    participant User
    participant Client
    participant AS as IdP (Authorisation Server)
    participant RS as App API

    User->>Client: Click "Sign in with SSO"
    Client->>Client: Generate code_verifier + SHA-256 → code_challenge
    Client->>AS: GET /authorize?response_type=code&code_challenge=S256...
    AS->>User: Login prompt (if no SSO session)
    User->>AS: Authenticate + approve scopes
    AS->>Client: 302 redirect → ?code=auth_code&state=xyz
    Client->>AS: POST /token?code=auth_code&code_verifier=...
    AS->>AS: SHA-256(verifier) == stored challenge?
    AS->>Client: { access_token, refresh_token, id_token }
    Client->>RS: GET /api/data (Authorization: Bearer access_token)
    RS->>Client: { protected resource }

Grant Types

GrantSSO Use CaseSecurity
Authorisation Code + PKCEWeb apps, SPAs, mobile SSO — the modern standardHighest. PKCE prevents code interception. Required by RFC 9700.
Client CredentialsServer-to-server SSO (service accounts, cron jobs)No user involved. Secrets must be rotated.
Device CodeCLI tools, smart TVs (SSO on headless devices)User authorises on a separate device. RFC 8628.
Refresh TokenLong-lived SSO sessions without re-authRotatable. Store securely; never expose to browser.

PKCE in Detail

PKCE protects SSO flows in public clients (SPAs, mobile apps) where a client secret cannot be stored securely:

  1. Client generates a random code_verifier (43–128 characters, RFC 7636 §4.1)
  2. Client computes code_challenge = BASE64URL(SHA-256(code_verifier)) (S256 method)
  3. Client sends code_challenge + code_challenge_method=S256 with the /authorize request
  4. After receiving the authorisation code, the client sends the raw code_verifier at the /token endpoint
  5. Server recomputes SHA-256(code_verifier) and rejects the request unless it matches the stored challenge

Scopes

Scopes define what each app can access in an SSO ecosystem: openid, profile, email, groups. They are a bounded, user-visible permission model. When an employee approves “Slack wants to read your profile and group membership”, that is scope consent.

OpenID Connect: How SSO Knows Who You Are

OAuth alone can tell an app “this token is valid”, but it cannot say who the user is. OIDC fills that gap. It adds (Core 1.0):

  • ID Token — A signed JWT with identity claims (sub, email, name, groups)
  • UserInfo Endpoint — Fetch additional claims using the access token
  • Discovery URL — https://idp.corp.com/.well-known/openid-configuration — standardised provider metadata per RFC 8414

Standard OIDC Scopes and Claims

ScopeStandard Claims
openidsub (required — unique user identifier)
profilename, given_name, family_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, updated_at
emailemail, email_verified
addressaddress (formatted, street_address, locality, region, postal_code, country)
phonephone_number, phone_number_verified

The ID Token

When a user SSOs into an app, the IdP returns an ID token like this:

{
  "iss": "https://idp.corp.com",
  "sub": "a1b2c3d4",
  "aud": "slack-app-id",
  "exp": 1718123456,
  "iat": 1718119856,
  "email": "alice@corp.com",
  "email_verified": true,
  "groups": ["engineering", "prod-access"],
  "nonce": "n-0S6_WzA2Mj"
}

Signed by the IdP (RS256, ES256). The app verifies the signature using the IdP’s JWKS endpoint (/.well-known/jwks.json). The sub claim is the persistent user identifier across all SSO apps — the IdP guarantees it never changes for that user.

Token Types in SSO

TokenFormatLifespanSSO Role
Access TokenOpaque or JWTMinutesAuthorises API calls on behalf of the user
Refresh TokenOpaqueDays/monthsObtains new access tokens without re-prompting
ID TokenJWT onlyMinutesProves who the user is and when they authenticated

SSO Session Management

After the initial authentication, the IdP maintains a session (usually via a cookie on the IdP domain). When the user opens a new SSO-linked app:

  1. The new app redirects to the IdP’s /authorize endpoint
  2. The IdP detects the existing session cookie
  3. The IdP immediately redirects back with an authorisation code — no password prompt
  4. The app exchanges the code for tokens and signs the user in

This is silent authentication — the user clicks once and is signed into every app without further prompts. If the IdP session expires, the user is prompted once and all apps regain access on next redirect.

Security Considerations for SSO

  • Always use PKCE with authorisation code flow — prevents code interception attacks even without a client secret. Required by OAuth 2.1 and RFC 9700 for all public clients.
  • Validate the iss (issuer) claim — ensure the token comes from your corporate IdP, not a rogue provider
  • Use nonce in OIDC — prevents replay attacks on the ID token
  • Validate token signatures — fetch the IdP’s JWKS and verify every token
  • Short token lifetimes — access tokens in minutes, refresh tokens with rotation. If a device is lost, short-lived tokens limit exposure
  • Never expose access tokens to browser JavaScript for confidential clients — they are backend-to-backend credentials
  • Use PAR (Pushed Authorisation Requests, RFC 9126) — sends the authorisation request body via a back-channel, hiding parameters from the browser URL
  • Use JARM (JWT Secured Authorisation Response Mode, RFC 9902) — signs the authorisation response to prevent tampering
  • Bind tokens to the TLS session via s_hash or DPoP (RFC 9449) to prevent token export and replay

Why Every Enterprise Standardises on OIDC for SSO

  1. Single Sign-On (SSO) — One login, all apps. The IdP session eliminates password fatigue.
  2. Federated identity — Authenticate against any standards-compliant IdP (Azure AD, Okta, Keycloak, Google). No vendor lock-in.
  3. Fine-grained access — Scopes and claims let IT control exactly what each app can see and do.
  4. Centralised audit trail — The IdP logs every authentication event across every app in one place.
  5. Zero-trust ready — Short-lived tokens replace long-lived VPNs. Every request carries verifiable proof of identity.
  6. Just-in-time provisioning — Apps receive user attributes (name, email, groups) via OIDC claims on first SSO, enabling automatic account creation.

OIDC is the authentication layer behind Kubernetes clusters (kube-apiserver + OIDC), SCIM provisioning (SCIM uses OAuth for API security), and FIDO2 passkey authentication.


OAuth 2.0 and OIDC are the foundation of modern identity. Every SaaS app, every enterprise portal, and every zero-trust architecture relies on them. Understanding the flow — tokens, scopes, grants, and signatures — is table stakes for anyone building secure software.

References