Setup OpenID Connect (OIDC)
Configure OpenID Connect for modern, secure authentication in your AI applications and MCP servers.
Overview
OpenID Connect (OIDC) is an identity layer built on top of OAuth 2.0. It provides a standardized way to authenticate users and obtain their identity information. OIDC is ideal for AI applications that need secure user authentication with modern web standards.
Benefits of OIDC
- Standardized Protocol: Industry-standard authentication
- Secure by Default: Built-in security best practices
- Rich User Information: Access to user profile and claims
- Token-Based: Stateless authentication with JWT tokens
- Federation Support: Connect with multiple identity providers
OIDC Configuration
1. Enable OIDC in Authsec
Configure OIDC settings in your Authsec dashboard:
- Navigate to Applications → Your AI App
- Go to the Authentication tab
- Enable OpenID Connect
- Configure the following settings:
{
"oidc_conformant": true,
"token_endpoint_auth_method": "client_secret_post",
"response_types": ["code", "id_token", "token"],
"grant_types": ["authorization_code", "refresh_token", "client_credentials"],
"id_token_signed_response_alg": "RS256"
}
2. Configure Scopes
Define the information your application needs:
{
"allowed_scopes": [
"openid", // Required for OIDC
"profile", // User profile information
"email", // User email address
"ai:access", // Custom AI access scope
"mcp:tools", // MCP tool access
"offline_access" // Refresh token capability
]
}
3. Set Redirect URIs
Configure allowed callback URLs:
{
"redirect_uris": [
"https://yourapp.com/auth/callback",
"https://yourapp.com/silent-auth",
"http://localhost:3000/callback" // For development
],
"post_logout_redirect_uris": [
"https://yourapp.com/logout",
"https://yourapp.com/"
]
}
Implementation
Web Application Flow
1. Authorization Request
import { AuthsecClient } from '@authsec/sdk';
const authsec = new AuthsecClient({
domain: 'your-domain.authsec.com',
clientId: 'your-client-id',
redirectUri: 'https://yourapp.com/callback'
});
// Redirect user to authorization server
function login() {
const authUrl = authsec.buildAuthorizeUrl({
response_type: 'code',
scope: 'openid profile email ai:access',
state: generateRandomState(), // CSRF protection
nonce: generateRandomNonce() // Replay attack protection
});
window.location.href = authUrl;
}
2. Handle Authorization Response
// Handle the callback from Authsec
async function handleCallback(req, res) {
try {
const { code, state } = req.query;
// Verify state parameter (CSRF protection)
if (state !== getStoredState()) {
throw new Error('Invalid state parameter');
}
// Exchange authorization code for tokens
const tokens = await authsec.exchangeCodeForTokens({
code: code,
redirect_uri: 'https://yourapp.com/callback'
});
// Validate and decode ID token
const idToken = await authsec.verifyIdToken(tokens.id_token);
// Store tokens securely (server-side session or secure cookies)
req.session.accessToken = tokens.access_token;
req.session.refreshToken = tokens.refresh_token;
req.session.user = idToken;
res.redirect('/dashboard');
} catch (error) {
console.error('OIDC callback error:', error);
res.redirect('/login?error=auth_failed');
}
}
Single Page Application (SPA) Flow
1. Initialize OIDC Client
import { AuthsecWebClient } from '@authsec/web-sdk';
const authsec = new AuthsecWebClient({
domain: 'your-domain.authsec.com',
clientId: 'your-spa-client-id',
redirectUri: window.location.origin + '/callback',
responseType: 'code',
scope: 'openid profile email ai:access',
usePKCE: true // Enable PKCE for security
});
2. Login Flow
// Login with redirect
async function login() {
try {
await authsec.loginWithRedirect();
} catch (error) {
console.error('Login failed:', error);
}
}
// Handle redirect callback
async function handleRedirectCallback() {
try {
const result = await authsec.handleRedirectCallback();
console.log('Login successful:', result.user);
// User is now authenticated
showDashboard();
} catch (error) {
console.error('Callback handling failed:', error);
showLoginError();
}
}
3. Silent Authentication
// Check if user is already authenticated
async function checkAuth() {
try {
const user = await authsec.getUser();
if (user) {
console.log('User is authenticated:', user);
return user;
} else {
// Try silent authentication
const silentUser = await authsec.getTokenSilently();
return silentUser;
}
} catch (error) {
console.log('User not authenticated');
return null;
}
}
Server-to-Server Flow
For AI agents and MCP servers that operate without user interaction:
// Client credentials flow for service authentication
async function authenticateService() {
try {
const tokenResponse = await authsec.clientCredentialsGrant({
scope: 'ai:generate ai:analyze mcp:tools',
audience: 'https://api.yourapp.com'
});
return tokenResponse.access_token;
} catch (error) {
console.error('Service authentication failed:', error);
throw error;
}
}
Token Validation
Validate Access Tokens
// Middleware to validate access tokens
async function validateAccessToken(req, res, next) {
try {
const token = extractTokenFromHeader(req.headers.authorization);
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
// Validate token with Authsec
const validationResult = await authsec.validateAccessToken(token, {
audience: 'https://api.yourapp.com',
issuer: `https://${process.env.AUTHSEC_DOMAIN}/`
});
if (validationResult.valid) {
req.user = validationResult.claims;
next();
} else {
return res.status(401).json({ error: 'Invalid token' });
}
} catch (error) {
console.error('Token validation error:', error);
return res.status(500).json({ error: 'Authentication error' });
}
}
Validate ID Tokens
// Validate ID tokens (for user authentication)
async function validateIdToken(idToken) {
try {
const decoded = await authsec.verifyIdToken(idToken, {
issuer: `https://${process.env.AUTHSEC_DOMAIN}/`,
audience: process.env.AUTHSEC_CLIENT_ID,
clockTolerance: 60 // Allow 60 seconds clock skew
});
// Verify custom claims if needed
if (decoded.nonce && decoded.nonce !== getStoredNonce()) {
throw new Error('Invalid nonce');
}
return decoded;
} catch (error) {
console.error('ID token validation failed:', error);
throw error;
}
}
User Information
Get User Profile
// Get user information from userinfo endpoint
async function getUserInfo(accessToken) {
try {
const userInfo = await authsec.getUserInfo(accessToken);
return {
id: userInfo.sub,
name: userInfo.name,
email: userInfo.email,
picture: userInfo.picture,
customClaims: {
aiAccess: userInfo['ai:access'],
mcpTools: userInfo['mcp:tools']
}
};
} catch (error) {
console.error('Failed to get user info:', error);
throw error;
}
}
Custom Claims
Add custom claims to ID tokens:
// Configure custom claims in Authsec dashboard or via API
const customClaims = {
"ai_permissions": {
"models": ["gpt-4", "claude-3"],
"rate_limit": 1000,
"features": ["generate", "analyze", "summarize"]
},
"mcp_access": {
"tools": ["web_search", "calculator", "file_access"],
"resources": ["documents", "images"],
"context_retention": "24h"
}
};
Logout
Logout Implementation
// Logout user and revoke tokens
async function logout() {
try {
// Get current tokens
const accessToken = getAccessToken();
const refreshToken = getRefreshToken();
// Revoke refresh token
if (refreshToken) {
await authsec.revokeToken(refreshToken);
}
// Clear local session
clearTokens();
// Redirect to Authsec logout URL
const logoutUrl = authsec.buildLogoutUrl({
returnTo: 'https://yourapp.com/',
client_id: process.env.AUTHSEC_CLIENT_ID
});
window.location.href = logoutUrl;
} catch (error) {
console.error('Logout error:', error);
// Clear local session anyway
clearTokens();
window.location.href = '/';
}
}
Security Considerations
PKCE (Proof Key for Code Exchange)
Always use PKCE for public clients:
// PKCE is automatically enabled for SPAs
const authsec = new AuthsecWebClient({
domain: 'your-domain.authsec.com',
clientId: 'your-spa-client-id',
usePKCE: true // Enabled by default
});
State Parameter
Use state parameter to prevent CSRF attacks:
function generateAndStoreState() {
const state = generateRandomString(32);
sessionStorage.setItem('auth_state', state);
return state;
}
function verifyState(receivedState) {
const storedState = sessionStorage.getItem('auth_state');
sessionStorage.removeItem('auth_state');
return storedState === receivedState;
}
Nonce Parameter
Use nonce to prevent replay attacks:
function generateAndStoreNonce() {
const nonce = generateRandomString(32);
sessionStorage.setItem('auth_nonce', nonce);
return nonce;
}
function verifyNonce(idToken) {
const storedNonce = sessionStorage.getItem('auth_nonce');
sessionStorage.removeItem('auth_nonce');
const decoded = jwt.decode(idToken);
return decoded.nonce === storedNonce;
}
Testing OIDC Integration
Unit Tests
// Test OIDC configuration
describe('OIDC Configuration', () => {
test('should build correct authorization URL', () => {
const authUrl = authsec.buildAuthorizeUrl({
response_type: 'code',
scope: 'openid profile',
state: 'test-state',
nonce: 'test-nonce'
});
expect(authUrl).toContain('response_type=code');
expect(authUrl).toContain('scope=openid%20profile');
expect(authUrl).toContain('state=test-state');
});
test('should validate ID tokens correctly', async () => {
const mockIdToken = 'eyJ...'; // Mock JWT
const result = await authsec.verifyIdToken(mockIdToken);
expect(result).toHaveProperty('sub');
expect(result).toHaveProperty('aud');
expect(result).toHaveProperty('exp');
});
});
Integration Tests
// Test full OIDC flow
describe('OIDC Flow Integration', () => {
test('should complete authorization code flow', async () => {
// Simulate authorization code callback
const mockCode = 'test-auth-code';
const tokens = await authsec.exchangeCodeForTokens({
code: mockCode,
redirect_uri: 'http://localhost:3000/callback'
});
expect(tokens).toHaveProperty('access_token');
expect(tokens).toHaveProperty('id_token');
expect(tokens).toHaveProperty('token_type', 'Bearer');
});
});
Troubleshooting
Common Issues
- Invalid Redirect URI: Ensure callback URLs match exactly
- Scope Not Granted: Check if requested scopes are configured
- Token Expired: Implement proper token refresh logic
- Nonce Mismatch: Verify nonce handling in ID token validation
- CORS Errors: Configure allowed origins in Authsec dashboard
Debug Tips
- Enable Debug Logging: Set
debug: truein SDK configuration - Check Network Tab: Inspect HTTP requests and responses
- Validate JWT Tokens: Use jwt.io to decode and inspect tokens
- Verify Configuration: Double-check client settings in Authsec dashboard
- Test with Postman: Use Postman to test API endpoints directly
Getting Help
- Check the OIDC specification
- Review Authsec dashboard logs
- Contact support with specific error messages
- Use browser developer tools to debug client-side issues