OIDC Hybrid Flow — Combining ID Token and Access Token in One Request
In this tutorial, you will learn about OIDC Hybrid Flow. We cover key concepts, practical examples, and best practices to help you master this topic.
The OIDC hybrid flow combines aspects of the authorization code and implicit flows by returning an ID token (and optionally an access token) directly from the authorization endpoint alongside the authorization code.
What You'll Learn
- How the hybrid flow works and its three response type variants
- When to use the hybrid flow vs authorization code flow
- Security considerations specific to the hybrid flow
Why It Matters
In the standard authorization code flow, you must exchange the code for tokens before seeing any user identity. The hybrid flow provides an ID token immediately, allowing your app to display the user's name or avatar during the token exchange, improving perceived performance.
Real-World Use
Doda Browser uses the hybrid flow with response_type=code id_token during login. The ID token arrives directly in the front channel, so the browser can immediately greet the user by name while the backend exchanges the authorization code for a refresh token in the background.
sequenceDiagram
participant Browser
participant OIDC as OIDC Provider
participant Backend
Browser->>OIDC: Auth Request (response_type=code id_token)
OIDC-->>Browser: ID Token + Authorization Code
Browser->>Browser: Verify ID Token (immediate)
Browser->>Backend: Send Authorization Code
Backend->>OIDC: Code + Secret -> Tokens
OIDC-->>Backend: Access + Refresh Token
Backend-->>Browser: Session Established
Hybrid Flow Response Types
The hybrid flow has three variants based on which tokens are returned from the authorization endpoint:
# response_type=code id_token
# Returns: Authorization Code + ID Token
params_code_id_token = {
"response_type": "code id_token",
"client_id": "doda-browser",
"redirect_uri": "https://doda.example.com/callback",
"scope": "openid profile email",
"state": "abc123",
"nonce": "xyz789"
}
# response_type=code token
# Returns: Authorization Code + Access Token
params_code_token = {
"response_type": "code token",
"client_id": "doda-browser",
"redirect_uri": "https://doda.example.com/callback",
"scope": "openid profile email",
"state": "abc123",
"nonce": "xyz789"
}
# response_type=code id_token token
# Returns: Authorization Code + ID Token + Access Token
params_full = {
"response_type": "code id_token token",
"client_id": "doda-browser",
"redirect_uri": "https://doda.example.com/callback",
"scope": "openid profile email",
"state": "abc123",
"nonce": "xyz789"
}
Handling the Hybrid Flow Callback
// Client-side handling of hybrid flow callback
function handleHybridFlowCallback() {
const urlParams = new URLSearchParams(window.location.hash.substring(1));
const idToken = urlParams.get('id_token');
const accessToken = urlParams.get('access_token');
const code = urlParams.get('code');
const state = urlParams.get('state');
// Verify state matches
const storedState = sessionStorage.getItem('oidc_state');
if (state !== storedState) {
console.error('State mismatch');
return;
}
// Decode ID token (client-side validation only)
const payload = JSON.parse(atob(idToken.split('.')[1]));
console.log('User:', payload.name);
// Send code to backend for token exchange
fetch('/api/auth/exchange', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, state })
}).then(res => res.json()).then(data => {
console.log('Session established:', data.sessionId);
});
}
Server-Side Token Exchange
from flask import Flask, request, session
import requests
app = Flask(__name__)
@app.route('/api/auth/exchange', methods=['POST'])
def exchange_code():
code = request.json.get('code')
token_response = requests.post(
"https://accounts.example.com/token",
data={
"grant_type": "authorization_code",
"code": code,
"redirect_uri": "https://doda.example.com/callback",
"client_id": "doda-browser",
"client_secret": "your-client-secret"
}
)
tokens = token_response.json()
session['access_token'] = tokens['access_token']
session['refresh_token'] = tokens.get('refresh_token')
return {"status": "ok", "sessionId": session.sid}
Common Mistakes
1. Trusting Client-Side ID Token Verification
The ID token returned in the fragment is visible to JavaScript. Verify it on the server side after token exchange for critical operations.
2. Using Hybrid Flow Without PKCE
The hybrid flow exposes tokens in the URL fragment. Use PKCE to prevent authorization code interception even though the code is also in the front channel.
3. Choosing the Wrong Response Type
response_type=code id_token token returns the most data but exposes the access token in the URL fragment. Use code id_token if you only need immediate identity.
4. Ignoring the Fragment Encoding
Tokens in the URL fragment are not sent to the server. Handle fragment Parsing on the client side and send the authorization code to the server programmatically.
5. Not Clearing the Fragment
After processing the callback, clear the URL fragment to prevent tokens from being visible in the browser history.
Practice Questions
- What three response types are available in the hybrid flow?
- What is the advantage of receiving an ID token immediately?
- Why should you still exchange the code on the server side?
- Does the hybrid flow support PKCE?
- What security risk is specific to the hybrid flow?
Answers
code id_token,code token,code id_token token. 2. Immediate user identity display during token exchange. 3. To get a refresh token and verify identity server-side. 4. Yes, PKCE is recommended. 5. Tokens appear in the URL fragment, visible in browser history and JavaScript.
Challenge
Build a hybrid flow implementation that demonstrates all three response types and logs the timing difference between when the ID token is available (front channel) vs when the access token is available (back channel).
FAQ
Mini Project
Build a single-page application demonstrating all three hybrid flow variants with a mock OIDC provider. Show the timing and payload differences between each variant, including fragment parsing, code exchange, and session establishment.
What's Next
- Learn about OIDC session management for maintaining user sessions
- Explore logout mechanisms including RP-initiated and OP-initiated logout
- Continue to session logout and post-logout redirects
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro