Skip to main content

Understanding OAuth & Open ID Connect (OIDC)

Confused by authentication? Learn how OpenID Connect (OIDC) acts as an identity layer on top of OAuth 2.0 to securely verify users without the headache.

Updated 12 min readMarkdown
Understanding OAuth & Open ID Connect (OIDC)

Have you ever wondered what actually happens behind the scenes when you click that "Sign in with Google" button?

It’s a lot like checking into a massive music festival. Instead of carrying your passport, credit cards, and ticket to every single stage, you show your ID once at the front gate to get a secure, tamper-proof wristband. From that point on, every stage manager instantly knows exactly who you are just by looking at it.

While developers often mistake OAuth 2.0 as the tool for this job, the real magic happens because of OpenID Connect (OIDC).

In this guide, we are going to break down how OIDC acts as the web’s ultimate identity layer and why it's the gold standard for secure authentication.

Understanding OAuth Flow

1. What is OAuth?

OAuth (Open Authorization) is a security standard that allows an application to access your data or accounts on another service without needing your password.

OAuth enables secure, limited access to user data across different applications without exposing user passwords and access features like "Sign in with Google" or "Allow App to Access Contacts" .

"OAuth acts like a digital valet key or a secure electronic hotel keycard. It provides a way to delegate specific permissions to apps you trust."


2. Why do we need OAuth?

We need OAuth because sharing passwords with third-party applications creates a massive security risk.

  • Exposed Master Passwords : Users must type their primary passwords directly into third-party apps, exposing them to untrusted developers.

  • Widespread Credential Theft : If a poorly secured third-party app gets hacked, cybercriminals steal actual user passwords to breach other websites.

  • Unlimited Account Privileges : Connected apps gain total control over the account, allowing them to read private messages, delete data, or make unauthorized purchases.

  • Endless Data Access : Shared passwords give third-party apps permanent access to user data with no built-in expiration dates.


3. Difference between OAuth and OAuth2.0

OAuth

  • Cryptographic Nightmare (Signature Complexity)

  • Poor Mobile and Desktop Support

  • No Short-Lived Tokens (High Security Risk)

  • Monolithic Server Architecture

  • Lack of Clear Separation of Duties (Roles)

OAuth2.0

  • Shifted Security to HTTPS (Fixed the Cryptographic Nightmare)

  • Introduced Multiple Grant Types (Fixed Mobile & Desktop Support)

  • Created Access & Refresh Tokens (Fixed the Lifespan Risk)

  • Split Authorization from Resources (Fixed the Monolithic Architecture)

  • Formalized Scopes and Clients (Fixed the Unclear Roles)


Understanding Open ID Connect (OIDC)

1. OIDC Layers (Conceptual Stack)

Application (Your App)
↑
OIDC (Identity Layer)
↑
OAuth 2.0 (Authorization)
↑
HTTP / TLS (Transport Security)

2. What is Open ID Connect

OpenID Connect (OIDC) is an identity verification layer built directly on top of the OAuth 2.0 framework that allows third-party applications to authenticate who a user is.

Core Components

  • User : User trying to sign in the application having OAuth2.0 which is registered on Authorization Server.

  • Client : Application registered on Authorization Server (frontend/backend)

"We will discuss it later how OIDC client can be frontend or backend."

  • Authorization Server : Application which handles sign in and issue tokens (basically provide Authentication flow) , e.g Google cloud, clerk, auth0, ...

You can even see my own OIDC Provider (ProtoAuth)


Endpoints (Discovery-Based)

OIDC standardizes endpoints via discovery route on /.well-known/openid-configuration

Core Architecture Fields

  • issuer : The absolute URL identifying the authorization server. It must exactly match the iss claim inside generated tokens.

  • authorization_endpoint : The sign in page URL where users enter credentials and consent to share data.

  • token_endpoint : The backend server URL where applications exchange authorization codes for access and refresh tokens.

  • userinfo_endpoint : A protected API that returns profile information about the authenticated user.

  • jwks_uri : The location of the public keys needed to verify the cryptographic signatures of incoming JSON Web Tokens (JWTs).

  • revocation_endpoint : The route used to immediately invalidate active tokens, usually when a user sign out.

Supported Capabilities Arrays

  • scopes_supported : The list of all data permissions the server allows an application to request.

  • response_types_supported : The authorized workflows (like Authorization Code or Implicit flows) available for obtaining tokens.

  • subject_types_supported : The method used to generate user identifiers (e.g., public uses the same ID across all client apps).

  • id_token_signing_alg_values_supported : The cryptographic algorithms supported for signing the identity token (RS256, HS256, etc.).

  • token_endpoint_auth_methods_supported : The allowed client authentication schemes when talking to the token endpoint, such as basic auth or post parameters.

  • claims_supported : The individual pieces of user data (like name or email) that can be embedded directly inside the identity token payload.

{
  "issuer": "https://identity.example.com",
  "authorization_endpoint": "https://example.com",
  "token_endpoint": "https://example.com",
  "userinfo_endpoint": "https://example.com",
  "revocation_endpoint": "https://example.com",
  "jwks_uri": "https://example.com",
  "scopes_supported": [
    "openid",
    "profile",
    "email",
    "offline_access"
  ],
  "response_types_supported": [
    "code",
    "token",
    "id_token"
  ],
  "subject_types_supported": [
    "public"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "token_endpoint_auth_methods_supported": [
    "client_secret_basic",
    "client_secret_post"
  ],
  "claims_supported": [
    "iss",
    "sub",
    "aud",
    "exp",
    "iat",
    "auth_time",
    "acr",
    "amr",
    "name",
    "given_name",
    "family_name",
    "email"
  ]
}

The issuer, authorization_endpoint, token_endpoint, userinfo_endpoint, jwks_uri endpoints are necessary for any Authorization Server.


ID Token (JWT Structure)

Unlike an OAuth Access Token (which is usually unreadable string to the app), an OIDC ID Token is always a JSON Web Token (JWT). It is split into three parts separated by dots (Header.Payload.Signature) and Base64Url encoded.

When your app decodes the central payload of an ID Token, it sees a standardized set of key-value pairs called Claims. These are divided into core security claims and user profile claims.

{ 
    "iss": "https://google.com",
    "sub": "10928374651029384756",
    "aud": "my-client-app-id-123",
    "exp": 1780704000,
    "iat": 1780700400,
    "auth_time": 1780700350,
    "acr": "urn:mace:incommon:iap:silver",

    "name": "Alex Smith",
    "email": "alex.smith@example.com",
    "email_verified": true 
}
  • iss (Issuer): The unique URL of the Identity Provider (IdP) that generated the token.

  • sub (Subject): A permanent, unique, non-recyclable identifier for the user. Never use emails as unique keys; always use sub.

  • aud (Audience): The Client ID of your specific application. Your app must reject the token if this doesn't match your ID.

  • exp / iat: The expiration time and issued-at time (Unix timestamps).

  • auth_time: The exact time the user physically typed their password. Useful if your app requires re-authentication for sensitive actions (like changing billing info).

  • acr (Authentication Context Class Reference): States how the user logged in (e.g., if they used Multi-Factor Authentication/MFA).


3. Difference b/w OIDC and OAuth2.0

OAuth2.0

  1. Authorization : Grants a third-party application limited access to your resources without exposing your credentials.

  2. Access Token

  3. OAuth 2.0 (Hotel Keycard) : It grants you access to the room, but it does not know your name or identity.

OIDC

  1. Authentication : Verifies the identity of the end-user and passes basic profile information (like name, email, and picture) to the application.

  2. Built on top of OAuth 2.0 (OIDC = OAuth2.0 + Identity layer)

  3. ID Token(JWT) + Access Token

  4. OIDC (Passport) : It contains your photo, name, and verified details to prove exactly who you are.


💡
Just try to go through the steps of OIDC flow. You really dont need to remember all the payloads i'm giving in an api endpoint. You should know why are we giving that specific field in the endpoint.

4. Core OIDC Protocol Flow (Important)

Discovery

Client fetches config through standard discovery endpoint

GET /.well-known/openid-configuration

Authorization Request

Redirect user to the authorization_endpoint with necessary query payload such as client_id, redirect_uri, scope

GET https://example.com/o/authenticate?
  response_type=code&
  client_id=client_123&
  redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&
  scope=openid%20profile%20email&
  state=af0ifjsldkj&
  nonce=n-0S6_WzA2Mj&
  code_challenge=CODE_CHALLENGE&
  code_challenge_method=S256
  • response_type=code : Tells the authorization server to return a temporary Authorization Code rather than tokens directly.

  • client_id=client_123 : Allows the authorization server to identify which app is requesting access and apply its specific security settings.

  • redirect_uri=https://example.com : Delivers the Authorization Code safely back to your app. It must match the server's pre-registered list to prevent hackers from stealing the code.

  • scope=openid profile email : openid triggers the OpenID Connect identity flow. profile and email request access to the user's basic profile details and email address.

Security & Anti-Tampering Parameters

  • state=af0ifjsldkj : Protects against Cross-Site Request Forgery (CSRF) attacks. The client verifies that the returned state matches the sent state.

  • nonce=n-0S6_WzA2Mj : Prevents replay attacks. The authorization server embeds this exact string inside the final ID token, allowing the client to verify the token was generated specifically for this request.

PKCE (Proof Key for Code Exchange) Parameters

  • code_challenge=CODE_CHALLENGE : Prevents malicious apps from intercepting and using the Authorization Code. The server holds this challenge and compares it against the raw secret when your app exchanges the code for tokens.

  • code_challenge_method=S256 : Tells the server that the challenge was generated using SHA-256 hashing, which is the secure, industry-standard method.


User Authentication

User signs in at provider. Now , provider may show signin, signup, consent page.


Authorization Code Returned

redirect_uri?code=abc123


Token Exchange

POST https://example.com/o/token?
    code=abc123&
    client_id=client_id_123&
    client_secret=client_secret_123&
    redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&
    grant_type=authorization_code&
    code_verifier=my_secret_random_string_12345

Endpoint & Protocol Fields

  • POST https://example.com : Targets the authorization server's secure token endpoint via a back-channel network connection, keeping sensitive tokens out of browser history.

  • grant_type=authorization_code : Instructs the server to process this request using the Authorization Code grant flow, signaling that an authorization code is being traded for tokens.

Grant Types in OIDC :

  1. authorization_code

  2. implicit (deprecated)

  3. client_credentials

  4. password (deprecated)

  5. device_code

Verification & Matching Fields

  • code=abc123 : Supplies the temporary, single-use ticket received from the browser redirection step, proving the user successfully authenticated.

  • redirect_uri=https://example.com : Acts as a security checkpoint by ensuring this exact string matches the redirect URI provided during the initial authorization request.

Application Authentication

  • client_id=client_id_123 : Identifies the specific application making the request against the server's registration database.

  • client_secret=client_secret_123 : Authenticates the backend application server using a private password, preventing malicious actors from impersonating the application.

PKCE Security Verification

  • code_verifier=my_secret_random_string_12345 : Provides the raw, unhashed secret string that the server hashes and checks against the original code_challenge to block authorization code interception.

Response

{
  id_token,
  access_token,
  refresh_token
}

ID Token Validation

Client must verify signature (using jwks_uri)

  1. iss matches provider

  2. aud matches client_id

  3. exp not expired


User Session Created

  1. Extract user from id_token

  2. Store session (cookie / JWT / state)


UserInfo Endpoint

Instead of decoding ID token fully

GET /userinfo
Authorization: Bearer access_token

5. OIDC Flows

  1. Authorization Code Flow (Gold Standard)

  2. Authorization Code + PKCE (Modern Standard)

  3. Hybrid Flow

  4. Implicit Flow (Deprecated)

I hope you remember that i told to explain a point later, on how provider client can be backend or frontend. This is exactly where you are gonna understand how.

💡
What i have used in this blog in the Core OIDC Protocol Flow is the Authorization Code + PKCE which is the modern standard now.

1.Authorization Code Flow

  • Core Idea

Frontend gets a code, backend exchanges it for tokens.

  • Flow

Step 1 : Redirect

response_type=code

GET https://example.com/o/authenticate?
  response_type=code&
  client_id=client_123&
  redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&
  scope=openid%20profile%20email&
  state=af0ifjsldkj&
  nonce=n-0S6_WzA2Mj

Step 2 : User Authenticates (handles by OIDC provider)

Step 3: Code Returned

redirect_uri?code=abc123

Step 4: Backend Exchange

POST https://example.com/o/token?
    code=abc123&
    client_id=client_id_123&
    client_secret=client_secret_123&
    redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&
    grant_type=authorization_code

No PKCE → vulnerable to interception

Response

{
  id_token,
  access_token,
  refresh_token
}

2.Authorization Code + PKCE

  • Why PKCE Exists ?

Originally, public clients (SPA/mobile) had no client_secret.

So, to ensure safety against intercepted code reuse PKCE modal was introduced.

You dont need client_secret if your application is a public client(SPA/mobile), but you do need it if your application is a confidential client (a traditional web application with a secure backend server).

  • Flow
  1. Generate
code_verifier = random string
code_challenge = hash(code_verifier)
  1. Send Challenge

response_type=code

GET https://example.com/o/authenticate?
  response_type=code&
  client_id=client_123&
  redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&
  scope=openid%20profile%20email&
  state=af0ifjsldkj&
  nonce=n-0S6_WzA2Mj&
  code_challenge=CODE_CHALLENGE&
  code_challenge_method=S256
  1. Exchange
POST https://example.com/o/token?
    code=abc123&
    client_id=client_id_123&
    client_secret=client_secret_123&
    redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&
    grant_type=authorization_code&
    code_verifier=my_secret_random_string_12345

Internally, Authorization server checks

hash(code_verifier) == code_challenge

If it found the mismatch then it rejects the incoming request.


3.Hybrid Flow

  • Core Idea

Immediate token & authorization code

  • Flow
  1. /authorize?

response_type=code id_token

GET https://example.com/o/authenticate?
  response_type=code%20id_token
  client_id=client_123&
  redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&
  scope=openid%20profile%20email&
  state=af0ifjsldkj&
  nonce=n-0S6_WzA2Mj
  1. /token?
POST https://example.com/o/token?
    code=abc123&
    client_id=client_id_123&
    client_secret=client_secret_123&
    redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&
    grant_type=authorization_code
  • Why It Exists ?

Reduce latency (early identity), frontend has to validate id_token.

Still allow backend validation, code exchange happen on backend (secure).


4.Implicit Flow

  • Core Idea

Tokens returned directly in URL

No client_secret is needed since tokens are directly being served from authorization server.

  • Flow
  1. /authorize?

response_type=id_token token

GET https://authorization-server.com
  ?response_type=token
  &client_id=your_public_client_id
  &redirect_uri=https://your-app.com
  &scope=read_profile
  &state=random_anti_csrf_string

Response

//302 redirect
https://your-app.com
  #id_token=eyJhbGciOiJSUzI1NiIs...
  &access_token=eyJhbGciOiJSUzI1NiIs...
  &token_type=Bearer
  &expires_in=3600
  &state=xyz123

Comparison :

Flow What Changes
Authorization Code code → backend → tokens
Code + PKCE code(pkce secured) → backend → tokens(pkce verified) (Same + extra security)
Implicit tokens → frontend (tokens directly in browser)
Hybrid Code → frontend → backend → tokens (mix of both)

You can see how grant_type decides whether the client will be frontend or backend and hence changes redirect_uri according to them.

50 views

No comments yet. Be the first to comment.