/** * OIDC configuration enhancement and self-healing * Automatically fixes common configuration issues and provides helpful guidance */ export interface OidcEnhancementResult { config: any; fixes: string[]; warnings: string[]; } /** * Enhance OIDC configuration with smart defaults and auto-fixes */ export function enhanceOidcConfig(config: any): OidcEnhancementResult { const enhanced = { ...config }; const fixes: string[] = []; const warnings: string[] = []; // Auto-add groups scope if missing if (enhanced.scope && !enhanced.scope.includes('groups')) { enhanced.scope = enhanced.scope.trim() + ' groups'; fixes.push('✓ Added "groups" to scope for role mapping'); } // Auto-detect optimal scope based on issuer if (!enhanced.scope && enhanced.issuer) { enhanced.scope = detectOptimalScope(enhanced.issuer); fixes.push(`✓ Auto-detected optimal scope: ${enhanced.scope}`); } // Auto-generate redirect_uri if missing if (!enhanced.redirect_uri) { const baseUrl = process.env.PUBLIC_URL || process.env.HEADPLANE_URL || 'http://localhost:3000'; enhanced.redirect_uri = `${baseUrl}/admin/oidc/callback`; fixes.push(`✓ Auto-generated redirect_uri: ${enhanced.redirect_uri}`); } // Provider-specific warnings and optimizations if (enhanced.issuer) { const providerInsights = getProviderInsights(enhanced.issuer); warnings.push(...providerInsights); } // Environment variable guidance const envVars = getRecommendedEnvVars(); if (envVars.length > 0) { warnings.push( '💡 For easier configuration, consider using environment variables:', ); warnings.push(...envVars); } return { config: enhanced, fixes, warnings }; } /** * Detect optimal OIDC scope based on identity provider */ function detectOptimalScope(issuer: string): string { const baseScope = 'openid email profile'; if (!issuer) return baseScope; // Provider-specific optimizations if (issuer.includes('accounts.google.com')) { return baseScope; // Google Workspace needs special setup for groups } if (issuer.includes('login.microsoftonline.com')) { return baseScope + ' groups'; // Azure AD supports groups scope } if (issuer.includes('keycloak') || issuer.includes('auth.')) { return baseScope + ' groups roles'; // Keycloak supports both } if (issuer.includes('okta.com') || issuer.includes('oktapreview.com')) { return baseScope + ' groups'; // Okta supports groups scope } if (issuer.includes('auth0.com')) { return baseScope + ' groups'; // Auth0 supports groups scope } // Safe default for unknown providers return baseScope + ' groups'; } /** * Get provider-specific insights and configuration tips */ function getProviderInsights(issuer: string): string[] { const insights: string[] = []; if (issuer.includes('accounts.google.com')) { insights.push( 'â„šī¸ Google: Groups require Google Workspace admin console configuration', ); insights.push( ' Consider using: HEADPLANE_ADMIN_GROUPS="admin@yourcompany.com"', ); } if (issuer.includes('login.microsoftonline.com')) { insights.push( 'â„šī¸ Azure AD: Add "groups" claim to token configuration in Azure portal', ); insights.push(' May require GroupMember.Read.All API permission'); } if (issuer.includes('keycloak')) { insights.push( 'â„šī¸ Keycloak: Configure group membership mapper in client settings', ); insights.push(' Groups appear in resource_access or realm_access claims'); } if (issuer.includes('okta.com')) { insights.push( 'â„šī¸ Okta: Enable group claims in authorization server settings', ); insights.push(' Groups appear in standard "groups" claim'); } if (issuer.includes('auth0.com')) { insights.push('â„šī¸ Auth0: Configure group claims in rules or actions'); insights.push(' Groups appear in custom namespace or "groups" claim'); } return insights; } /** * Get recommended environment variables for easier configuration */ function getRecommendedEnvVars(): string[] { const hasEnvMapping = [ 'HEADPLANE_OWNER_GROUPS', 'HEADPLANE_ADMIN_GROUPS', 'HEADPLANE_NETWORK_ADMIN_GROUPS', 'HEADPLANE_IT_ADMIN_GROUPS', 'HEADPLANE_AUDITOR_GROUPS', ].some((env) => process.env[env]); if (hasEnvMapping) { return []; // Already using environment variables } return [ ' HEADPLANE_ADMIN_GROUPS="admin,administrators,managers"', ' HEADPLANE_OWNER_GROUPS="ceo,cto,founders"', ' HEADPLANE_NETWORK_ADMIN_GROUPS="devops,network,sre"', ]; } /** * Validate configuration and provide helpful error messages */ export function validateOidcConfig(config: any): { valid: boolean; errors: string[]; } { const errors: string[] = []; if (!config.issuer) { errors.push('❌ Missing issuer URL'); errors.push( ' Example: https://your-provider.com or https://login.microsoftonline.com/tenant/v2.0', ); } if (!config.client_id) { errors.push('❌ Missing client_id'); errors.push(' This should be provided by your identity provider'); } if (!config.client_secret && !config.client_secret_path) { errors.push('❌ Missing client_secret or client_secret_path'); errors.push(' For security, consider using client_secret_path'); } // URL validation if (config.issuer && !isValidUrl(config.issuer)) { errors.push('❌ Invalid issuer URL format'); errors.push(' Must be a valid HTTPS URL'); } if (config.redirect_uri && !isValidUrl(config.redirect_uri)) { errors.push('❌ Invalid redirect_uri format'); errors.push(' Must be a valid HTTPS URL (or HTTP for localhost)'); } return { valid: errors.length === 0, errors, }; } function isValidUrl(urlString: string): boolean { try { const url = new URL(urlString); return ( url.protocol === 'https:' || (url.hostname === 'localhost' && url.protocol === 'http:') ); } catch { return false; } } /** * Create development OIDC configuration with mock provider */ export function createDevOidcConfig() { if (process.env.NODE_ENV !== 'development') { return null; } return { issuer: 'http://localhost:3001/dev-oidc', client_id: 'dev-headplane', client_secret: 'dev-secret', scope: 'openid email profile groups', redirect_uri: 'http://localhost:3000/admin/oidc/callback', mock_users: [ { sub: 'dev-admin', email: 'admin@dev.local', name: 'Dev Admin', groups: ['admin', 'developers'], }, { sub: 'dev-user', email: 'user@dev.local', name: 'Dev User', groups: ['developers'], }, { sub: 'dev-owner', email: 'owner@dev.local', name: 'Dev Owner', groups: ['ceo', 'founders'], }, ], }; }