Quizzr Logo

OAuth 2.0 & OIDC

Extending OAuth 2.0 with OpenID Connect for Identity Verification

Understand how the OIDC layer introduces ID Tokens and the UserInfo endpoint to handle authentication alongside authorization.

SecurityIntermediate12 min read

The Identity Gap: Moving from OAuth to OIDC

Software engineers often find themselves at a crossroads when deciding how to handle user logins versus user permissions. OAuth 2.0 was originally designed as a delegation protocol, allowing a third-party application to access resources on behalf of a user without seeing their credentials. While it excels at managing access tokens for APIs, it intentionally lacks a standardized way to convey who the user actually is.

When developers tried to use raw OAuth 2.0 for authentication, they often resorted to proprietary hacks to fetch user details. This led to a fragmented ecosystem where every provider had a different user info format and a different way to verify session state. This lack of consistency made it difficult to build secure, interoperable identity solutions across different platforms and services.

OpenID Connect was introduced as an identity layer built directly on top of the OAuth 2.0 framework. It provides a standardized set of rules for authenticating users and sharing their profile information. By extending the existing OAuth flows, it allows applications to receive both an access token for resource authorization and an ID token for user identification.

OAuth 2.0 is for authorization (accessing stuff), while OIDC is for authentication (knowing who someone is). Using OAuth alone for identity is like using a valet key as a passport; it gets you in the car, but it does not prove your nationality.

Standardizing Identity with Scopes

In OIDC, the presence of the openid scope in the authorization request signals to the server that the client wants identity information. This scope is mandatory for any OIDC flow and triggers the generation of an ID token alongside the traditional access token. Without this specific scope, the server treats the request as a standard OAuth 2.0 authorization request.

Additional standardized scopes like profile, email, address, and phone number allow the client to request specific categories of user data. These scopes map to predefined claims within the ID token or the UserInfo endpoint. This predictability allows developers to write generic code that handles identity from multiple providers like Google, GitHub, or Okta without significant modification.

Deep Dive into the ID Token

The ID Token is the core component of the OIDC layer and is formatted as a JSON Web Token. Unlike the access token, which is often opaque and intended for a resource server, the ID Token is transparent and intended for the client application. It contains assertions about the authentication event, such as the time of login and the identity of the user.

Every ID Token consists of three parts: a header, a payload, and a signature. The header defines the algorithm used for signing, such as RS256. The payload contains the claims, which are key-value pairs representing the user data and token metadata. The signature is used by the client to verify that the token was issued by a trusted provider and has not been tampered with during transit.

jsonDeconstructed ID Token Payload
1{
2  "iss": "https://identity.provider.com",
3  "sub": "user_uuid_987654321",
4  "aud": "client_id_00112233",
5  "exp": 1712543000,
6  "iat": 1712539400,
7  "nonce": "random_string_for_security",
8  "email": "dev.engineer@example.com",
9  "name": "Jane Developer"
10}

The sub claim is particularly critical as it represents a unique, never-recycled identifier for the user within the provider system. Developers should always use this claim as the primary key for linking local user accounts to external identities. Relying on mutable attributes like email addresses can lead to security vulnerabilities if a user changes their email or if an old email is reassigned to a new person.

Validating Claims and Signatures

Validation is the most important step after receiving an ID Token. The client must first fetch the provider public keys from a well-known configuration endpoint to verify the signature. If the signature is invalid, the token must be rejected immediately to prevent identity spoofing attacks.

Beyond the signature, the client must verify the aud (audience) claim to ensure the token was meant for them. If the audience does not match the client ID, it means the token was intended for a different application. Additionally, the exp (expiration) claim ensures that the token is still valid and not a replayed credential from the past.

Expanding Identity with the UserInfo Endpoint

While the ID Token is perfect for immediate authentication, it is often kept small to ensure it can be passed efficiently in headers or cookies. When an application needs comprehensive user profile data, it turns to the UserInfo endpoint. This is a protected REST API that returns detailed claims about the authenticated user in a JSON format.

Accessing the UserInfo endpoint requires a valid Access Token that was granted during the OIDC flow. This creates a clear separation of concerns: the ID Token provides a snapshot of identity, while the UserInfo endpoint provides a gateway to richer, dynamic profile attributes. This pattern prevents the ID Token from becoming bloated with rarely used metadata.

javascriptFetching User Profile Data
1async function getUserProfile(accessToken) {
2  const response = await fetch('https://identity.provider.com/userinfo', {
3    headers: {
4      'Authorization': `Bearer ${accessToken}`
5    }
6  });
7
8  if (!response.ok) {
9    throw new Error('Failed to fetch user info');
10  }
11
12  // Returns standardized claims like given_name, family_name, and locale
13  return await response.json();
14}

Using the UserInfo endpoint also allows the identity provider to enforce more granular privacy controls. For instance, a provider might include basic identity in the ID Token but require a separate consent check before releasing sensitive address or birthdate information through the UserInfo response. This gives users better visibility into what data is being shared with third-party apps.

Managing Token Lifecycles

Developers must distinguish between the lifespan of the ID Token and the Access Token. The ID Token represents a point-in-time authentication event and often has a short validity window. If the user session needs to persist longer, the application should rely on its own session management or use refresh tokens to obtain new credentials.

Caching UserInfo responses can improve application performance but introduces the risk of stale data. If a user updates their profile on the identity provider side, your local cache might not reflect those changes until the next full login. Implementing a sensible cache TTL (Time To Live) is essential for balancing performance with data accuracy.

Advanced Security and Implementation Patterns

Implementing OIDC correctly requires attention to several advanced security parameters that prevent common web attacks. One such parameter is the nonce, a random string sent in the initial request and returned in the ID Token. The client must verify that the returned nonce matches the original to mitigate replay attacks where an attacker tries to reuse a stolen token.

Another vital tool is the state parameter, which protects against Cross-Site Request Forgery. By including a unique, unguessable value in the authorization request and validating it upon return, the client ensures that the authentication response corresponds to a request initiated by the same user agent. This prevents attackers from tricking a user into logging into the application with the attacker credentials.

  • Always use the Authorization Code Flow with PKCE for both mobile and web applications to prevent code injection.
  • Ensure all communication with the identity provider occurs over TLS 1.2 or higher to protect tokens in transit.
  • Do not store ID Tokens or Access Tokens in local storage; use secure, httpOnly cookies to prevent XSS-based token theft.
  • Regularly rotate signing keys and implement automated JWKS (JSON Web Key Set) discovery in your client libraries.

When designing the architecture, consider the trade-offs between the implicit flow and the authorization code flow. The implicit flow, while simpler for browser-based apps, is now widely discouraged because it returns tokens directly in the URL fragment. Modern best practices dictate using the authorization code flow with Proof Key for Code Exchange for almost all client types.

Handling Multi-Tenancy

In multi-tenant applications, the issuer validation becomes more complex. You cannot hardcode a single issuer URL if your application supports logins from multiple independent organizations. In these cases, your validation logic must dynamically verify the issuer against a list of approved tenants in your database.

Mistakes in multi-tenant validation are a frequent source of security breaches. If an application accepts a valid ID Token from an untrusted issuer just because the signature is correct, an attacker could create their own identity provider and gain unauthorized access. Always validate that the issuer belongs to the expected set of trusted authorities.

We use cookies

Necessary cookies keep the site working. Analytics and ads help us improve and fund Quizzr. You can manage your preferences.