Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

OpenID Connect (OIDC) Explained

By Kokil Thapa | Last reviewed: September 2026

OpenID Connect (OIDC) Explained starts with one fact most teams miss: OAuth 2.0 answers authorization, not identity. It tells your app what a user may do. It does not reliably tell you who that user is. OpenID Connect adds a thin identity layer on top of OAuth 2.0. Your app receives a signed ID token with stable subject claims. That is why every major IdP — Google, Microsoft Entra ID, Auth0, Keycloak — ships OIDC endpoints alongside OAuth. If you build REST APIs with third-party login, client portals, or CI pipelines that authenticate without long-lived keys, you will touch OIDC within your first sprint.

What Is OpenID Connect and How Does It Differ from OAuth 2.0?

OAuth 2.0 is an authorization framework. A resource owner grants a client limited access to protected resources. The access token proves that grant. OAuth alone does not standardise who the user is. Vendors invented custom /userinfo calls and proprietary profile fields. That fragmentation caused bugs, security gaps, and endless integration rework.

OpenID Connect standardises identity on top of OAuth. An OIDC provider (OP) exposes a discovery document at /.well-known/openid-configuration. That JSON lists endpoints for authorization, token exchange, JWKS keys, and optional userinfo. The relying party (RP) — your application — uses those URLs to complete login and verify tokens.

The core OIDC artefact is the ID token. It is a JWT signed by the OP. Standard claims include iss (issuer), sub (subject), aud (audience), exp, and iat. Optional claims carry email, name, and picture. Access tokens still handle API calls. ID tokens prove authentication events.

OpenID Connect StackOpenID Connect — identity + discovery + ID tokenOAuth 2.0 — authorization + access tokensHTTP + TLS — transport securityID Token JWTAccess TokenRefresh Token
OpenID Connect (OIDC) explained: identity sits above OAuth 2.0 and produces a signed ID token alongside access tokens.

Compare the three protocols engineers confuse most often. SAML remains common in enterprise SSO. OAuth powers delegated API access. OIDC bridges OAuth clients to standard identity claims.

ProtocolPrimary purposeMain artefactTypical transport
OAuth 2.0 / 2.1Authorization (scoped API access)Access tokenBearer header, form POST
OpenID ConnectAuthentication + profile claimsID token (JWT)Authorization code flow + token endpoint
SAML 2.0Enterprise federated SSOSAML assertion (XML)Browser POST binding, XML signatures

For a deeper OAuth versus OIDC breakdown, read our companion post on OAuth 2.1 vs OpenID Connect. OAuth 2.1 (the current consolidation effort) removes implicit flow and tightens redirect URI rules. OIDC inherits those constraints. Treat both specs as one login design surface in 2026.

Official references matter here. The OpenID Connect Core 1.0 specification defines mandatory behaviour. The OAuth 2.1 draft documents the authorization framework OIDC builds upon. Bookmark both before you wire production login.

How Does the OpenID Connect Authorization Code Flow Work?

The authorization code flow with PKCE is the default choice for web and mobile apps in 2026. It keeps tokens off the browser history. It binds each login attempt to a one-time code verifier. Implicit flow is deprecated. Password grant is forbidden for new public clients.

The sequence has six practical steps. Each step has a failure mode I have debugged on client portals and booking systems.

  1. Discover endpoints. Fetch /.well-known/openid-configuration from your OP base URL. Cache it for hours, not days. IdPs rotate keys and endpoints without fanfare.
  2. Build the authorize URL. Include response_type=code, client_id, redirect_uri, scope=openid profile email, state, and PKCE code_challenge.
  3. User authenticates at the OP. The browser redirects back with an authorization code and the same state you sent.
  4. Exchange the code server-side. POST to the token endpoint with code_verifier. Never expose your client secret in front-end JavaScript.
  5. Receive tokens. The JSON response contains id_token, access_token, and optionally refresh_token.
  6. Validate the ID token. Verify signature, issuer, audience, expiry, and nonce before you create a local session.
OIDC Authorization Code + PKCEBrowserYour App (RP)IdP (OP)1. Redirect /authorize + PKCE2. User login + consent3. Redirect with auth code4. Forward code5. POST /token + verifier6. ID + access tokens
OpenID Connect authorization code flow: PKCE protects public clients while the server-side token exchange keeps secrets off the browser.

Minimal authorize URL example

Replace placeholders with values from your OP registration panel. Store state and code_verifier in server-side session storage before redirecting.

GET https://login.example.com/oauth2/v2.0/authorize
  ?client_id=YOUR_CLIENT_ID
  &response_type=code
  &scope=openid%20profile%20email
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Fauth%2Fcallback
  &state=RANDOM_CSRF_TOKEN
  &nonce=RANDOM_NONCE
  &code_challenge=BASE64URL_SHA256_VERIFIER
  &code_challenge_method=S256

Token exchange on your backend

Perform this call from PHP, not from the browser. On a production Laravel application I typically wrap this in a dedicated service class and log failures with correlation IDs.

POST https://login.example.com/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=AUTH_CODE_FROM_CALLBACK
&redirect_uri=https://app.example.com/auth/callback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&code_verifier=ORIGINAL_PKCE_VERIFIER

GitHub Actions uses the same OIDC pattern for cloud deploys. Our guide on deploying to AWS from GitHub Actions with OIDC shows how CI workloads exchange short-lived tokens instead of storing static AWS keys. The mental model is identical: trust a signed token from a known issuer.

How Do You Validate an OpenID Connect ID Token in Production?

Receiving an ID token is not authentication. Validating it is. Skipping any check opens the door to token substitution, replay, and audience confusion attacks. I treat ID token validation like payment webhook verification: mandatory, logged, and tested.

Run these checks in order on every login callback.

  • Parse the JWT header. Read alg and kid. Reject alg=none immediately.
  • Fetch JWKS. Pull keys from the jwks_uri in discovery. Cache keys with a short TTL. Refresh on signature failure.
  • Verify the signature. Use the matching public key. PHP 8.3+ projects often use firebase/php-jwt or the OP's official SDK.
  • Validate claims. Confirm iss matches the OP, aud contains your client ID, exp is in the future, and iat is reasonable.
  • Match nonce. Compare token nonce to the value stored in session at authorize time.
  • Map sub to a local user. Create or update the account. Never use email alone as the primary key.
ID Token Validation PipelineReceive ID Token JWTVerify signature via JWKSCheck iss, aud, exp, iat, nonceExtract sub + profile claimsCreate session / issue app cookieReject: bad sigReject: claim mismatch
Production OpenID Connect ID token validation: signature verification and claim checks must complete before any session is created.

PHP validation sketch

This pattern mirrors what I ship on legal-tech portals where document access depends on verified identity. Use a maintained JWT library. Do not hand-roll crypto.

use Firebase\JWT\JWT;
use Firebase\JWT\JWK;

$jwks = json_decode(file_get_contents($discovery['jwks_uri']), true);
$keys = JWK::parseKeySet($jwks);

$decoded = JWT::decode($idToken, $keys);

if ($decoded->iss !== $expectedIssuer) {
    throw new AuthException('Invalid issuer');
}
if (!in_array($clientId, (array) $decoded->aud, true)) {
    throw new AuthException('Invalid audience');
}
if ($decoded->nonce !== session('oidc_nonce')) {
    throw new AuthException('Invalid nonce');
}

Decode JWT segments during debugging with a Base64 encoder and decoder. Never paste production tokens into public tools. Use local copies only. For structured logging of token metadata (not raw tokens), a JSON formatter helps inspect claim payloads in staging.

Rate-limit your callback route. OIDC login endpoints attract credential-stuffing and code replay attempts. Our write-up on API rate limiting and abuse prevention applies directly to /auth/callback handlers.

How Should Laravel Applications Implement OpenID Connect?

Laravel 12 and Laravel 13 applications on PHP 8.3+ have several integration paths. Pick based on whether you control the IdP, the RP, or both. Wrong package choice creates migration pain later.

Socialite with an OIDC driver suits Google, GitHub, and Microsoft login buttons. Install socialiteproviders/microsoft-azure or similar provider packages. Override scopes to include openid. Map the returned user array to Eloquent.

Passport as an OIDC provider works when your Laravel app is the identity source for other services. Passport issues OAuth tokens natively. Pair it with a package like laravel-openid-connect when you must expose standard OIDC endpoints to third-party RPs.

Sanctum for first-party SPAs covers cookie-based auth for your own Vue or Alpine front end. Sanctum is not a full OIDC OP. It does not replace external IdP login for enterprise SSO. Read the Laravel Sanctum documentation to understand where it stops and OIDC begins.

On portals like Mijar Law Associates and Court Marriage In Nepal, I keep external OIDC login separate from local staff accounts. Clients authenticate via Google or Microsoft. Internal admins use email/password with 2FA. Two auth guards, one user table with a nullable oidc_sub column.

Suggested users table columns

Schema::table('users', function (Blueprint $table) {
    $table->string('oidc_sub')->nullable()->unique();
    $table->string('oidc_issuer')->nullable();
    $table->timestamp('last_oidc_login_at')->nullable();
});

Store refresh tokens encrypted if you keep them. Rotate on logout. Never log raw tokens. Secrets belong in .env and in vault tooling. Our notes on Ansible Vault for secrets cover the same discipline for server config that OIDC client secrets demand.

For greenfield enterprise application development, document your OIDC contract early. List required scopes, claim mappings, session lifetime, and logout behaviour. Ambiguity here costs weeks during UAT.

What Are Common OpenID Connect Security Mistakes to Avoid?

Most OIDC incidents I troubleshoot are configuration errors, not novel exploits. The protocol is sound. Implementations cut corners.

OIDC Security: Wrong vs RightCommon Mistakes• Skip nonce validation• Trust email without proof• Accept alg=none tokens• Store tokens in localStorage• Wildcard redirect URIs• No PKCE on public clients• Cache JWKS for daysCorrect Practice• Bind nonce to session• Use sub as stable ID• Allowlist signing algs• HttpOnly secure cookies• Exact redirect URI match• PKCE S256 everywhere• Short JWKS cache TTLfix
OpenID Connect security mistakes that break production login — and the correct OIDC patterns that prevent them.

Mistake 1: Treating the access token as proof of identity. Access tokens authorise API calls. They may be opaque strings with no standard claims. Always validate the ID token for login decisions.

Mistake 2: Skipping logout (RP-initiated logout). Clearing your local session leaves an OP session alive. Users click "login" and silently re-enter the wrong account. Implement end-session endpoints when your OP supports them.

Mistake 3: Loose redirect URI registration. A wildcard subdomain invites authorization code interception. Register exact HTTPS URLs. Use separate client IDs for staging and production.

Mistake 4: Long-lived JWKS cache without refresh. Key rotation happens. Retry validation once with a forced JWKS fetch before you fail the login.

Fold OIDC regression tests into your release pipeline. Hit the callback with expired tokens, wrong aud, and tampered signatures. Testing and optimization should cover auth paths, not just checkout forms.

Operational teams also need runbooks. Document which IdP tenant owns production, who can rotate client secrets, and how to revoke a compromised refresh token. That belongs alongside support and maintenance procedures, not in a forgotten wiki page.

Key Takeaways

  • OpenID Connect adds standard identity claims and ID tokens on top of OAuth 2.0 authorization.
  • Use authorization code flow with PKCE for every public client in 2026; implicit flow is dead.
  • Validate ID token signature, issuer, audience, expiry, and nonce before creating any local session.
  • Map users by OIDC sub plus iss, not by email address alone.
  • Keep client secrets server-side, store sessions in HttpOnly cookies, and rate-limit callback routes.
  • Document scopes, claim mappings, and logout behaviour before enterprise UAT begins.

People Also Ask

Is OpenID Connect the same as OAuth?

No. OAuth 2.0 grants scoped access to APIs through access tokens. OpenID Connect adds authentication semantics: a standard ID token, discovery document, and profile scopes like openid, profile, and email. You almost always implement both together, but they solve different problems.

What is the difference between an ID token and an access token?

An ID token is a JWT that proves the user authenticated at a specific time. Your application consumes it directly. An access token authorises calls to resource APIs. It may be opaque. Never send an ID token to downstream microservices as a bearer credential.

Do I need OpenID Connect if I already use SAML?

Enterprise SSO often still uses SAML for legacy apps. Modern SPAs, mobile clients, and cloud CI pipelines prefer OIDC because JSON/JWT tooling is simpler and OAuth scopes fit API-first architectures. Many teams run both during migration.

Can Laravel Sanctum replace OpenID Connect?

Sanctum handles first-party SPA and mobile token auth for your own Laravel app. It does not make Laravel a standards-compliant OIDC provider for third-party login. Use Sanctum for internal APIs and OIDC drivers or Passport when federated identity is required.

Put OpenID Connect to Work on Your Next Project

You now have a working mental model for OpenID Connect (OIDC) Explained end to end: discovery, PKCE, token exchange, ID token validation, and the security checks that separate a demo login from a production-ready one. OIDC is not exotic infrastructure. It is the default identity layer for modern web apps, CI pipelines, and client portals in 2026.

If you are adding SSO to a Laravel portal, wiring GitHub Actions OIDC to cloud deploys, or untangling a broken callback handler, the fastest path is a focused architecture review before code spreads across controllers. I have integrated OIDC on legal-tech platforms, booking systems, and custom software projects where document access depends on verified identity.

Contact us for help designing or hardening your OpenID Connect login flow. You can also browse the portfolio for client portals that rely on secure authentication, or read more on the blog about OAuth, API security, and deployment patterns. For background on my approach to production systems, see about me and our Linux system administration notes for server-side TLS and session hardening that OIDC depends on.

Frequently Asked Questions

OpenID Connect is an identity protocol built on OAuth 2.0. Your app receives a signed ID token with stable subject claims so you know who authenticated, not just what API access they were granted.

No. OAuth 2.0 grants scoped access to APIs through access tokens. OpenID Connect adds authentication semantics: a standard ID token, discovery document, and profile scopes like openid, profile, and email. You almost always implement both together, but they solve different problems.

An ID token is a JWT that proves the user authenticated at a specific time. Your application consumes it directly. An access token authorises calls to resource APIs. It may be opaque. Never send an ID token to downstream microservices as a bearer credential.

The relying party fetches the discovery document, builds an authorize URL with response_type=code, openid scopes, state, nonce, and PKCE code_challenge, then sends the user to the OpenID provider. After login, the provider redirects back with an authorization code. Your backend POSTs to the token endpoint with the code_verifier and client secret, receives id_token and access_token, validates the ID token, and only then creates a local session. Never exchange the code from browser JavaScript.

PKCE binds each login attempt to a one-time code_verifier paired with a code_challenge sent in the authorize request. The token endpoint rejects the exchange unless the verifier matches. That stops authorization code interception on public web and mobile clients. Authorization code flow with PKCE is the default choice in 2026; implicit flow is deprecated and password grant is forbidden for new public clients.

Receiving a token is not authentication—validation is. Parse the JWT header and reject alg=none. Fetch JWKS from the jwks_uri in discovery, cache keys briefly, and verify the signature with a maintained library such as firebase/php-jwt on PHP 8.3+. Confirm iss matches your provider, aud contains your client ID, exp is future, iat is reasonable, and nonce matches the session value from authorize time. Map sub to a local user only after every check passes.

Every OpenID provider exposes JSON at /.well-known/openid-configuration listing canonical endpoints: authorization, token exchange, JWKS keys, and optional userinfo. Your relying party reads this once per login setup instead of hard-coding URLs. Cache it for hours, not days, because providers rotate keys and endpoints without announcement. Wrong or stale discovery URLs are a common first failure when wiring Google, Microsoft Entra ID, Auth0, or Keycloak login.

SAML 2.0 targets enterprise federated SSO with XML assertions, browser POST bindings, and XML signatures. OpenID Connect sits on OAuth 2.0 and uses JSON, JWT ID tokens, and authorization code flow with PKCE. SAML remains common in legacy enterprise apps. Modern SPAs, mobile clients, and cloud CI pipelines prefer OIDC because JWT tooling is simpler and OAuth scopes fit API-first architectures. Many teams run both protocols during migration rather than forcing an immediate cutover.

Not always immediately, but most new integrations should target OIDC. SAML still serves legacy enterprise applications, while OIDC is the default identity layer for modern web apps, mobile clients, REST APIs, and CI pipelines that authenticate with short-lived tokens instead of long-lived keys. If you are adding a Laravel client portal, SPA, or GitHub Actions deploy pipeline alongside existing SAML apps, OIDC is the practical choice. Running both during migration is normal until legacy SAML consumers are retired.

Pick the path that matches your role. Socialite with an OIDC driver suits Google, GitHub, and Microsoft login—install provider packages like socialiteproviders/microsoft-azure and include openid in scopes. Passport plus laravel-openid-connect works when your Laravel app is the identity source for other services. Sanctum covers first-party SPA cookie auth but does not make Laravel a standards-compliant OIDC provider. On client portals I keep external OIDC login separate from local staff accounts, storing oidc_sub, oidc_issuer, and last_oidc_login_at on the users table.

No. Sanctum handles first-party SPA and mobile token authentication for your own Laravel application. It does not expose standard OIDC discovery, ID token issuance, or federated login for third-party relying parties. Use Sanctum for internal APIs and cookie-based front ends you control. When clients must sign in through Google, Microsoft Entra ID, or another external identity provider, use Socialite with an OIDC driver or Passport when your Laravel app must act as the provider.

Most incidents are configuration errors, not protocol flaws. Teams treat opaque access tokens as proof of identity instead of validating the ID token. They skip RP-initiated logout, leaving provider sessions alive so users silently re-enter the wrong account. Wildcard redirect URIs invite authorization code interception—register exact HTTPS URLs and separate staging from production client IDs. Long JWKS caches without refresh break login after key rotation; retry once with a forced fetch before failing. Rate-limit /auth/callback and regression-test expired tokens, wrong aud, and tampered signatures.

Access tokens authorise API calls to resource servers. They may be opaque strings with no standard identity claims and are not designed for your application to consume as proof of who logged in. OpenID Connect exists precisely because OAuth alone left vendors inventing custom userinfo calls and proprietary profile fields. Always validate the signed ID token—checking signature, issuer, audience, expiry, and nonce—before creating a session. Using access tokens for login decisions opens the door to token substitution and audience confusion attacks.

The sub claim is the stable subject identifier scoped to a specific issuer. Email is optional, can change, and is not guaranteed unique across tenants or providers. After ID token validation, create or update local accounts using sub plus iss, not email alone. On production portals I add a nullable unique oidc_sub column alongside oidc_issuer to link returning federated users reliably. Email still displays in the UI, but it should not be your primary authentication key when multiple IdPs or account merges are possible.

At minimum include openid, which tells the provider to return an ID token. Add profile and email when you need display name and mailbox claims in the token or via the userinfo endpoint. A typical authorize URL uses scope=openid profile email alongside response_type=code, client_id, redirect_uri, state, nonce, and PKCE parameters. Document required scopes, claim mappings, session lifetime, and logout behaviour before enterprise UAT—ambiguity here routinely costs weeks when stakeholders expect fields your token never carries.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: