OpenID Connect Examples
Explore practical code snippets and real-world scenarios demonstrating the integration of OpenID Connect with our platform. These examples cover common use cases, from basic authentication to advanced flows.
1. Basic Authentication Flow (Authorization Code Flow)
This example demonstrates the most common and recommended OpenID Connect flow, the Authorization Code Flow. It's suitable for web applications where the client secret can be kept confidential.
Client-Side (JavaScript)
// Configuration
const clientId = 'YOUR_CLIENT_ID';
const redirectUri = 'YOUR_REDIRECT_URI';
const authorizationEndpoint = 'https://your-auth-server.com/oauth2/authorize';
const tokenEndpoint = 'https://your-auth-server.com/oauth2/token';
const userInfoEndpoint = 'https://your-auth-server.com/oauth2/userinfo';
const scope = 'openid profile email';
function login() {
const state = generateRandomString();
const nonce = generateRandomString();
// Store state and nonce for verification
sessionStorage.setItem('oidc_state', state);
sessionStorage.setItem('oidc_nonce', nonce);
const authUrl = `${authorizationEndpoint}?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=${scope}&state=${state}&nonce=${nonce}`;
window.location.href = authUrl;
}
async function handleRedirect() {
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const receivedState = urlParams.get('state');
const storedState = sessionStorage.getItem('oidc_state');
if (!code || receivedState !== storedState) {
console.error('Authentication failed or state mismatch.');
return;
}
try {
const tokenResponse = await fetch(tokenEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: clientId,
redirect_uri: redirectUri,
code: code,
// client_secret is needed for confidential clients, not typically in JS
})
});
const tokenData = await tokenResponse.json();
sessionStorage.setItem('oidc_access_token', tokenData.access_token);
sessionStorage.setItem('oidc_id_token', tokenData.id_token);
sessionStorage.setItem('oidc_refresh_token', tokenData.refresh_token); // If issued
await getUserInfo(tokenData.access_token);
} catch (error) {
console.error('Error exchanging code for token:', error);
}
}
async function getUserInfo(accessToken) {
try {
const userInfoResponse = await fetch(userInfoEndpoint, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
const userInfo = await userInfoResponse.json();
console.log('User Info:', userInfo);
// Display user info or redirect
document.getElementById('userInfo').innerText = JSON.stringify(userInfo, null, 2);
} catch (error) {
console.error('Error fetching user info:', error);
}
}
function generateRandomString() {
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
}
// Call handleRedirect if this page is the redirect URI
if (window.location.search.includes('code=')) {
handleRedirect();
}
Server-Side (Conceptual - Node.js Example)
// This is a simplified server-side conceptual example
// It assumes you have a secure way to store client secrets
// and handle token exchange.
const express = require('express');
const bodyParser = require('body-parser');
const axios = require('axios'); // For making HTTP requests
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
const clientId = 'YOUR_CLIENT_ID';
const clientSecret = 'YOUR_CLIENT_SECRET'; // Keep this secure!
const redirectUri = 'YOUR_REDIRECT_URI';
const tokenEndpoint = 'https://your-auth-server.com/oauth2/token';
const userInfoEndpoint = 'https://your-auth-server.com/oauth2/userinfo';
const scope = 'openid profile email';
// Route to initiate login (redirects to auth server)
app.get('/login', (req, res) => {
const state = generateRandomString();
const nonce = generateRandomString();
req.session.oidc_state = state; // Store state in session
req.session.oidc_nonce = nonce; // Store nonce in session
const authUrl = `https://your-auth-server.com/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=${scope}&state=${state}&nonce=${nonce}`;
res.redirect(authUrl);
});
// Callback route for the authorization server
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
const storedState = req.session.oidc_state;
if (!code || state !== storedState) {
return res.status(400).send('Authentication failed or state mismatch.');
}
try {
const tokenResponse = await axios.post(tokenEndpoint, null, {
params: {
grant_type: 'authorization_code',
client_id: clientId,
client_secret: clientSecret,
redirect_uri: redirectUri,
code: code
}
});
const { access_token, id_token, refresh_token } = tokenResponse.data;
// Store tokens securely (e.g., in encrypted session or database)
req.session.access_token = access_token;
req.session.id_token = id_token;
req.session.refresh_token = refresh_token;
// Fetch user info
const userInfoResponse = await axios.get(userInfoEndpoint, {
headers: { 'Authorization': `Bearer ${access_token}` }
});
req.session.user_info = userInfoResponse.data; // Store user info
res.redirect('/dashboard'); // Redirect to a protected dashboard
} catch (error) {
console.error('Error exchanging code for token:', error.response ? error.response.data : error.message);
res.status(500).send('Error processing authentication.');
}
});
// Example protected route
app.get('/dashboard', (req, res) => {
if (!req.session.user_info) {
return res.redirect('/login');
}
res.send(`Welcome, ${req.session.user_info.name}!`);
});
function generateRandomString() {
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
}
// ... rest of your server setup (session management, etc.)
client_id
: Your application's identifier.redirect_uri
: Where the authorization server sends the user back after authentication.response_type=code
: Specifies the Authorization Code Flow.scope
: Permissions requested (e.g.,openid
for OIDC,profile
for user profile info).state
: Prevents CSRF attacks. Must be unique per request and verified on callback.nonce
: Used with the ID Token to mitigate replay attacks.- Token Endpoint: Used to exchange the
code
foraccess_token
andid_token
. - User Info Endpoint: Used to retrieve user profile information using the
access_token
.
2. Implicit Flow (For SPAs)
The Implicit Flow is simpler for Single Page Applications (SPAs) as it returns tokens directly from the authorization endpoint. However, it's less secure as tokens are exposed in the browser history and URL.
response_type=token id_token
is used, and the tokens are returned directly in the URL fragment. Not recommended for new applications if Authorization Code Flow with PKCE is an option.
Client-Side (JavaScript)
// Configuration
const clientId = 'YOUR_CLIENT_ID';
const redirectUri = 'YOUR_REDIRECT_URI';
const authorizationEndpoint = 'https://your-auth-server.com/oauth2/authorize';
const scope = 'openid profile email';
function loginImplicit() {
const state = generateRandomString();
const nonce = generateRandomString();
sessionStorage.setItem('oidc_state', state);
sessionStorage.setItem('oidc_nonce', nonce);
// response_type=token id_token for implicit flow
const authUrl = `${authorizationEndpoint}?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=token%20id_token&scope=${scope}&state=${state}&nonce=${nonce}`;
window.location.href = authUrl;
}
function handleImplicitRedirect() {
const hashParams = new URLSearchParams(window.location.hash.substring(1)); // Remove '#'
const accessToken = hashParams.get('access_token');
const idToken = hashParams.get('id_token');
const receivedState = hashParams.get('state');
const receivedNonce = hashParams.get('nonce'); // Often sent back in id_token claims
const storedState = sessionStorage.getItem('oidc_state');
const storedNonce = sessionStorage.getItem('oidc_nonce');
if (!accessToken || !idToken || receivedState !== storedState) {
console.error('Implicit flow authentication failed or state mismatch.');
return;
}
// Basic ID Token validation (more thorough validation needed in production)
const payload = JSON.parse(atob(idToken.split('.')[1]));
if (payload.nonce !== storedNonce) {
console.error('ID Token nonce mismatch.');
return;
}
sessionStorage.setItem('oidc_access_token', accessToken);
sessionStorage.setItem('oidc_id_token', idToken);
console.log('Implicit flow tokens received:', { accessToken, idToken });
// Proceed to fetch user info or update UI
// Note: UserInfo endpoint typically requires the access_token
// getUserInfo(accessToken); // If userInfo endpoint is available
}
function generateRandomString() {
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
}
// Call handleImplicitRedirect if this page is the redirect URI and has a hash
if (window.location.hash.includes('access_token=')) {
handleImplicitRedirect();
}
response_type=token id_token
.- Tokens are returned directly in the URL fragment (
#
). - No separate token exchange step.
- Less secure due to token exposure in URL/history.
3. Client-Credentials Flow (For Server-to-Server)
This flow is used when an application needs to access resources on its own behalf, without user delegation. It's common for backend services communicating with APIs.
Server-Side (Conceptual - Node.js Example)
// This is a server-side example as it requires client secret
const axios = require('axios');
const clientId = 'YOUR_CLIENT_ID';
const clientSecret = 'YOUR_CLIENT_SECRET'; // Keep this secure!
const tokenEndpoint = 'https://your-auth-server.com/oauth2/token';
const audience = 'your-api-audience'; // The API you want to access
async function getClientAccessToken() {
try {
const tokenResponse = await axios.post(tokenEndpoint, null, {
params: {
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret,
scope: 'read write', // Scopes relevant to the resource server
audience: audience
}
});
const { access_token } = tokenResponse.data;
console.log('Client Credentials Access Token:', access_token);
return access_token;
} catch (error) {
console.error('Error obtaining client credentials token:', error.response ? error.response.data : error.message);
throw error;
}
}
// Example usage:
// getClientAccessToken().then(token => {
// // Use the token to call an API
// axios.get('https://your-api.com/resource', { headers: { 'Authorization': `Bearer ${token}` } });
// });
grant_type=client_credentials
: Specifies this flow.client_id
&client_secret
: Used for client authentication.scope
: Permissions the client is requesting for itself.audience
: Specifies the target resource server.
4. Refreshing Tokens
Access tokens have a limited lifespan. This example shows how to use a refresh token to obtain a new access token without requiring the user to re-authenticate.
Server-Side (Conceptual - Node.js Example)
// Assumes you have stored the refresh token from an earlier login
// const storedRefreshToken = req.session.refresh_token;
const clientId = 'YOUR_CLIENT_ID';
const clientSecret = 'YOUR_CLIENT_SECRET'; // If required by your auth server
const tokenEndpoint = 'https://your-auth-server.com/oauth2/token';
async function refreshAccessToken(refreshToken) {
try {
const tokenResponse = await axios.post(tokenEndpoint, null, {
params: {
grant_type: 'refresh_token',
client_id: clientId,
// client_secret: clientSecret, // Include if needed for confidential clients
refresh_token: refreshToken,
// scope: 'openid profile email' // May be required to re-specify scopes
}
});
const { access_token, id_token, refresh_token } = tokenResponse.data; // Refresh token might be re-issued
console.log('New Access Token received:', access_token);
return { newAccessToken: access_token, newIdToken: id_token, newRefreshToken: refresh_token };
} catch (error) {
console.error('Error refreshing access token:', error.response ? error.response.data : error.message);
// Handle token expiration or invalid refresh token scenarios
throw error;
}
}
// Example usage:
// if (isTokenExpired(req.session.access_token)) { // You need a function to check expiration
// refreshAccessToken(req.session.refresh_token)
// .then(({ newAccessToken, newIdToken, newRefreshToken }) => {
// req.session.access_token = newAccessToken;
// req.session.id_token = newIdToken;
// req.session.refresh_token = newRefreshToken;
// // Retry the original API call with the new token
// })
// .catch(error => {
// // User needs to re-authenticate
// res.redirect('/login');
// });
// }
grant_type=refresh_token
.refresh_token
: The token obtained during the initial login.client_id
: Usually required.client_secret
may also be required.- The authorization server may issue a new refresh token along with the new access token.