// OIDC Client Wrapper using openid-client import { Issuer, generators } from 'openid-client'; import { authConfig } from '../config/auth.js'; let oidcClient = null; let issuer = null; /** * Initialize the OIDC client by discovering the issuer */ export async function initializeOIDC() { if (!authConfig.app.authEnabled) { console.log('[OIDC] Authentication disabled, skipping initialization'); return null; } try { console.log(`[OIDC] Discovering issuer: ${authConfig.oidc.issuer}`); issuer = await Issuer.discover(authConfig.oidc.issuer); console.log(`[OIDC] Discovered issuer: ${issuer.issuer}`); oidcClient = new issuer.Client({ client_id: authConfig.oidc.clientId, client_secret: authConfig.oidc.clientSecret, redirect_uris: [authConfig.oidc.redirectUri], response_types: ['code'], }); console.log('[OIDC] Client initialized successfully'); return oidcClient; } catch (error) { console.error('[OIDC] Failed to initialize client:', error.message); throw error; } } /** * Get the initialized OIDC client */ export function getClient() { if (!oidcClient) { throw new Error('OIDC client not initialized. Call initializeOIDC() first.'); } return oidcClient; } /** * Generate authorization URL for login */ export function getAuthorizationUrl(state, nonce, codeVerifier) { const client = getClient(); const codeChallenge = generators.codeChallenge(codeVerifier); return client.authorizationUrl({ scope: authConfig.oidc.scopes.join(' '), state, nonce, code_challenge: codeChallenge, code_challenge_method: 'S256', }); } /** * Exchange authorization code for tokens */ export async function exchangeCode(code, codeVerifier, nonce) { const client = getClient(); const tokenSet = await client.callback( authConfig.oidc.redirectUri, { code }, { code_verifier: codeVerifier, nonce } ); return tokenSet; } /** * Validate and decode ID token claims */ export function getIdTokenClaims(tokenSet) { return tokenSet.claims(); } /** * Get user info from the userinfo endpoint */ export async function getUserInfo(accessToken) { const client = getClient(); return await client.userinfo(accessToken); } /** * Refresh access token using refresh token */ export async function refreshTokens(refreshToken) { const client = getClient(); return await client.refresh(refreshToken); } /** * Get end session URL for logout */ export function getEndSessionUrl(idTokenHint, postLogoutRedirectUri) { const client = getClient(); return client.endSessionUrl({ id_token_hint: idTokenHint, post_logout_redirect_uri: postLogoutRedirectUri, }); } /** * Generate random state for CSRF protection */ export function generateState() { return generators.state(); } /** * Generate random nonce for ID token validation */ export function generateNonce() { return generators.nonce(); } /** * Generate code verifier for PKCE */ export function generateCodeVerifier() { return generators.codeVerifier(); } export default { initializeOIDC, getClient, getAuthorizationUrl, exchangeCode, getIdTokenClaims, getUserInfo, refreshTokens, getEndSessionUrl, generateState, generateNonce, generateCodeVerifier, };