loading
Preparing LoginRadius developer resources
Mission: Help enterprises accelerate digital transformation with our fully-managed Customer IAM technology.
Skip to main content

OpenID Connect Hybrid Flow

The OpenID Connect (OIDC) Hybrid Flow combines elements of both the Authorization Code Flow and the Implicit Flow. It allows the client to receive tokens directly from the authorization endpoint (front-channel) while still performing a secure back-channel token exchange. This makes it suitable for server-side applications that need an id_token immediately in the callback — for example, to establish a user session — while deferring the full access_token and refresh_token acquisition to a back-channel call.

This page covers the end-to-end Hybrid Flow as implemented by LoginRadius, including the Authorization Endpoint, front-channel token delivery, and the Token Exchange.

Overview

The flow operates in two distinct phases:

  1. Authorization Phase — runs in the browser. The client redirects the user to the LoginRadius OIDC Authorization endpoint. After the user authenticates, LoginRadius redirects back to the client's redirect_uri with an authorization_code and one or more tokens delivered as a URL fragment, depending on the response_type.
  2. Token Exchange Phase — runs server-side (back-channel). The client exchanges the authorization_code for an access_token, id_token, and optionally a refresh_token by making a direct POST call to the Token endpoint.

Supported response_type Values

The Hybrid Flow is identified by a response_type that combines code with one or more token identifiers:

response_typeReturned at Authorization EndpointReturned at Token Endpoint
code tokencode, access_tokenaccess_token, id_token, refresh_token
code id_tokencode, id_tokenaccess_token, id_token, refresh_token
code token id_tokencode, access_token, id_tokenaccess_token, id_token, refresh_token

When id_token is included in response_type, the nonce parameter is required in the authorization request.


Flow Diagram


Step-by-Step Walkthrough

Step 1 — Authorization Request

The client application redirects the user's browser to the LoginRadius OIDC Authorization endpoint with a response_type that combines code with one or more token identifiers.

Endpoint

GET https://{SiteURL}/service/oidc/{OIDCAppName}/authorize

SiteURL = Either <TenantName>.hub.loginradius.com or CustomDomain i.e. auth.your-app.com . OIDCAppName = your configured OIDC App name

Example Request

GET https://your-app.hub.loginradius.com/service/oidc/MyApp/authorize
?client_id=YOUR_OIDC_CLIENT_ID
&response_type=code%20token%20id_token
&scope=openid%20profile%20email
&redirect_uri=https://your-app.com/callback
&state=abc123xyz
&nonce=xyz987abc

nonce is required whenever id_token is included in response_type. It is embedded in the returned id_token to bind it to the client session and prevent replay attacks.


Step 2 — Session Check

Upon receiving the request, LoginRadius checks whether the user already has an active authenticated session.

  • If an active session is found → LoginRadius skips the login UI and immediately redirects to redirect_uri with the authorization code and any front-channel tokens.
  • If no active session → LoginRadius renders the login UI for the user to authenticate.

Step 3 — User Authentication

The user authenticates via the login page. Two login paths are supported:

  • Traditional Login — The user submits their credentials (email/password or phone).
  • Social Login — The user authenticates via a configured social provider. After successful authentication, LoginRadius generates a one-time, short-lived authorization_code along with the tokens corresponding to the requested response_type, and redirects back to the client's redirect_uri.

Step 4 — Authorization Response

LoginRadius redirects to the client callback with the authorization_code and any front-channel tokens delivered as a URL fragment (#).

The Hybrid Flow always delivers the authorization response as a URL fragment — even the code. Parameters arrive as # fragment values, not ? query parameters, and are only accessible to client-side JavaScript via window.location.hash. They are never sent to the server in the redirect request.

Success Response — response_type=code token id_token

HTTP 302
Location: https://your-app.com/callback
#code={authorization_code}
&access_token={access_token}
&token_type=Bearer
&id_token={id_token}
&expires_in=3600
&state=abc123xyz

Success Response — response_type=code id_token

HTTP 302
Location: https://your-app.com/callback
#code={authorization_code}
&id_token={id_token}
&state=abc123xyz

Success Response — response_type=code token

HTTP 302
Location: https://your-app.com/callback
#code={authorization_code}
&access_token={access_token}
&token_type=Bearer
&expires_in=3600
&state=abc123xyz

Error Response

HTTP 302
Location: https://your-app.com/callback
#error=access_denied
&error_description=...

Errors are returned as fragment parameters on the redirect_uri by default. This behavior can be changed by passing the response_mode parameter in the authorization request.


Step 5 — Token Exchange

The client backend makes a direct POST call to the LoginRadius Token endpoint to exchange the authorization code for tokens. This call never goes through the browser.

Endpoint

POST https://{SiteURL}/api/oidc/{OIDCAppName}/token

The token endpoint accepts the following content type:

Content-TypeDescription
application/x-www-form-urlencodedParameters sent as a URL-encoded form body

The client_id and client_secret are passed as a Base64-encoded Authorization header (Basic Base64(client_id:client_secret)). They must not be included in the request body.

POST /api/oidc/{OIDCAppName}/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic Base64(YOUR_OIDC_CLIENT_ID:YOUR_OIDC_CLIENT_SECRET)

grant_type=authorization_code
&code={authorization_code}
&redirect_uri=https://your-app.com/callback

Success Response

{
"access_token": "<LoginRadius JWT Access Token>",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "<Refresh Token>",
"id_token": "<JWT ID Token>"
}

Token Response Fields

FieldDescription
access_tokenLoginRadius JWT — use this to call protected APIs
token_typeAlways Bearer
expires_inLifetime of the access token in seconds
refresh_tokenLong-lived token to obtain new access tokens after expiry
id_tokenJWT containing identity claims about the authenticated user

c_hash and at_hash Claims

When tokens are returned from the authorization endpoint alongside a code or access_token, the id_token includes hash claims to cryptographically bind the tokens together. This prevents substitution attacks where a valid id_token is paired with a malicious code or access_token.

ClaimPresent whenDescription
c_hashcode is returned alongside id_token at the authorization endpointLeft half of the Base64URL-encoded SHA-256 hash of the authorization_code
at_hashaccess_token is returned alongside id_token at the authorization endpointLeft half of the Base64URL-encoded SHA-256 hash of the access_token

Clients must validate these hash claims when present:

// Validate c_hash / at_hash — Node.js example
const crypto = require('crypto');

function validateHash(token, hashClaim) {
// Hash the token with SHA-256
const digest = crypto.createHash('sha256').update(token).digest();
// Take the left half and Base64URL-encode it
const leftHalf = digest.subarray(0, digest.length / 2);
const computed = leftHalf.toString('base64url');
// Constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(computed),
Buffer.from(hashClaim)
);
}

// Usage
const isCodeValid = validateHash(authorizationCode, idTokenClaims.c_hash);
const isTokenValid = validateHash(accessToken, idTokenClaims.at_hash);

Error Handling

All errors are returned as fragment parameters appended to the client's redirect_uri:

https://your-app.com/callback#error=ERROR_CODE&error_description=Human+readable+message

Hybrid Flow vs Authorization Code Flow vs Implicit Flow

AttributeAuthorization Code FlowHybrid FlowImplicit Flow
response_typecodecode token, code id_token, code token id_tokentoken, id_token, token id_token
Tokens at authorization endpointNoneaccess_token and/or id_token (depending on response_type)access_token and/or id_token
Back-channel token exchange✅ Yes✅ Yes❌ No
refresh_token supported✅ Yes✅ Yes❌ No
client_secret required✅ Yes✅ Yes❌ No
c_hash / at_hash binding❌ Not applicable✅ Required when applicable❌ Not applicable
Primary use caseServer-side web appsServer-side apps needing immediate id_token + back-channel accessLegacy / deprecated
PKCE compatible✅ Yes✅ Yes❌ No

Security Considerations

The Hybrid Flow inherits front-channel token exposure risks from the Implicit Flow for tokens delivered in the authorization response. The back-channel token exchange mitigates the primary risks but the following points apply:

RiskDescription
Token in URL fragmentFront-channel tokens are exposed in the browser URL fragment, making them visible in browser history and accessible to client-side scripts. Validate and discard them promptly; rely on back-channel tokens for long-lived access.
id_token replayAlways validate the nonce claim in any id_token received at the authorization endpoint. Reject any id_token where the nonce does not match the value sent in the authorization request.
c_hash / at_hash bindingWhen id_token is returned alongside code or access_token at the authorization endpoint, the id_token includes c_hash and/or at_hash claims. Validate these to ensure the code and access_token were not substituted in transit.
state validationAlways validate the state parameter returned in the callback matches the value sent in the authorization request to prevent CSRF.
redirect_uri exact matchThe redirect_uri in the token request must exactly match the one used in the authorization request. Mismatches are rejected by LoginRadius.

For most new server-side applications, the Authorization Code Flow is preferred over the Hybrid Flow unless there is a specific requirement to receive an id_token immediately in the front channel.