|
|
@ -19,89 +19,6 @@ import { GoogleStrategy } from './google.strategy'; |
|
|
import { JwtStrategy } from './jwt.strategy'; |
|
|
import { JwtStrategy } from './jwt.strategy'; |
|
|
import { OidcStrategy } from './oidc.strategy'; |
|
|
import { OidcStrategy } from './oidc.strategy'; |
|
|
|
|
|
|
|
|
// ANSI color codes
|
|
|
|
|
|
const colors = { |
|
|
|
|
|
blue: '\x1b[34m', |
|
|
|
|
|
reset: '\x1b[0m', |
|
|
|
|
|
white: '\x1b[37m', |
|
|
|
|
|
yellow: '\x1b[33m' |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
function validateOidcConfiguration( |
|
|
|
|
|
configurationService: ConfigurationService |
|
|
|
|
|
): void { |
|
|
|
|
|
const missingVariables: string[] = []; |
|
|
|
|
|
|
|
|
|
|
|
// Common required variables for both configurations
|
|
|
|
|
|
const clientId = configurationService.get('OIDC_CLIENT_ID'); |
|
|
|
|
|
const clientSecret = configurationService.get('OIDC_CLIENT_SECRET'); |
|
|
|
|
|
const rootUrl = configurationService.get('ROOT_URL'); |
|
|
|
|
|
|
|
|
|
|
|
if (!clientId) { |
|
|
|
|
|
missingVariables.push('OIDC_CLIENT_ID'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!clientSecret) { |
|
|
|
|
|
missingVariables.push('OIDC_CLIENT_SECRET'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!rootUrl) { |
|
|
|
|
|
missingVariables.push('ROOT_URL'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check for automatic or manual configuration
|
|
|
|
|
|
const authorizationUrl = configurationService.get('OIDC_AUTHORIZATION_URL'); |
|
|
|
|
|
const issuer = configurationService.get('OIDC_ISSUER'); |
|
|
|
|
|
const tokenUrl = configurationService.get('OIDC_TOKEN_URL'); |
|
|
|
|
|
const userInfoUrl = configurationService.get('OIDC_USER_INFO_URL'); |
|
|
|
|
|
|
|
|
|
|
|
const hasAutomaticConfig = !!issuer; |
|
|
|
|
|
const hasManualConfig = authorizationUrl || tokenUrl || userInfoUrl; |
|
|
|
|
|
|
|
|
|
|
|
if (!hasAutomaticConfig && !hasManualConfig) { |
|
|
|
|
|
missingVariables.push( |
|
|
|
|
|
'OIDC_ISSUER (for automatic configuration) or OIDC_AUTHORIZATION_URL, OIDC_TOKEN_URL, OIDC_USER_INFO_URL (for manual configuration)' |
|
|
|
|
|
); |
|
|
|
|
|
} else if (!hasAutomaticConfig && hasManualConfig) { |
|
|
|
|
|
// Manual configuration: all three URLs are required
|
|
|
|
|
|
if (!authorizationUrl) { |
|
|
|
|
|
missingVariables.push('OIDC_AUTHORIZATION_URL'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!tokenUrl) { |
|
|
|
|
|
missingVariables.push('OIDC_TOKEN_URL'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!userInfoUrl) { |
|
|
|
|
|
missingVariables.push('OIDC_USER_INFO_URL'); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (missingVariables.length > 0) { |
|
|
|
|
|
const formattedVariables = missingVariables |
|
|
|
|
|
.map( |
|
|
|
|
|
(variable) => |
|
|
|
|
|
` ${colors.blue}${variable}:${colors.white} undefined${colors.reset}` |
|
|
|
|
|
) |
|
|
|
|
|
.join('\n'); |
|
|
|
|
|
|
|
|
|
|
|
const errorMessage = ` |
|
|
|
|
|
================================ |
|
|
|
|
|
${colors.yellow}Missing${colors.white} OIDC environment variables:${colors.reset} |
|
|
|
|
|
${formattedVariables} |
|
|
|
|
|
|
|
|
|
|
|
${colors.white}Configuration options:${colors.reset} |
|
|
|
|
|
1. Automatic: Set ${colors.blue}OIDC_ISSUER${colors.reset} (endpoints discovered automatically) |
|
|
|
|
|
2. Manual: Set ${colors.blue}OIDC_AUTHORIZATION_URL${colors.reset}, ${colors.blue}OIDC_TOKEN_URL${colors.reset}, ${colors.blue}OIDC_USER_INFO_URL${colors.reset} |
|
|
|
|
|
|
|
|
|
|
|
Both options require: ${colors.blue}ROOT_URL${colors.reset}, ${colors.blue}OIDC_CLIENT_ID${colors.reset}, ${colors.blue}OIDC_CLIENT_SECRET${colors.reset} |
|
|
|
|
|
================================ |
|
|
|
|
|
`;
|
|
|
|
|
|
Logger.error(errorMessage, 'OidcStrategy'); |
|
|
|
|
|
process.exit(1); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Module({ |
|
|
@Module({ |
|
|
controllers: [AuthController], |
|
|
controllers: [AuthController], |
|
|
imports: [ |
|
|
imports: [ |
|
|
@ -137,8 +54,6 @@ ${formattedVariables} |
|
|
return null; |
|
|
return null; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
validateOidcConfiguration(configurationService); |
|
|
|
|
|
|
|
|
|
|
|
const issuer = configurationService.get('OIDC_ISSUER'); |
|
|
const issuer = configurationService.get('OIDC_ISSUER'); |
|
|
const scope = configurationService.get('OIDC_SCOPE'); |
|
|
const scope = configurationService.get('OIDC_SCOPE'); |
|
|
|
|
|
|
|
|
@ -146,9 +61,24 @@ ${formattedVariables} |
|
|
configurationService.get('OIDC_CALLBACK_URL') || |
|
|
configurationService.get('OIDC_CALLBACK_URL') || |
|
|
`${configurationService.get('ROOT_URL')}/api/auth/oidc/callback`; |
|
|
`${configurationService.get('ROOT_URL')}/api/auth/oidc/callback`; |
|
|
|
|
|
|
|
|
let options: StrategyOptions; |
|
|
// Check for manual URL overrides
|
|
|
|
|
|
const manualAuthorizationUrl = configurationService.get( |
|
|
if (issuer) { |
|
|
'OIDC_AUTHORIZATION_URL' |
|
|
|
|
|
); |
|
|
|
|
|
const manualTokenUrl = configurationService.get('OIDC_TOKEN_URL'); |
|
|
|
|
|
const manualUserInfoUrl = |
|
|
|
|
|
configurationService.get('OIDC_USER_INFO_URL'); |
|
|
|
|
|
|
|
|
|
|
|
let authorizationURL: string; |
|
|
|
|
|
let tokenURL: string; |
|
|
|
|
|
let userInfoURL: string; |
|
|
|
|
|
|
|
|
|
|
|
// If all manual URLs are provided, use them; otherwise fetch from discovery
|
|
|
|
|
|
if (manualAuthorizationUrl && manualTokenUrl && manualUserInfoUrl) { |
|
|
|
|
|
authorizationURL = manualAuthorizationUrl; |
|
|
|
|
|
tokenURL = manualTokenUrl; |
|
|
|
|
|
userInfoURL = manualUserInfoUrl; |
|
|
|
|
|
} else { |
|
|
try { |
|
|
try { |
|
|
const response = await fetch( |
|
|
const response = await fetch( |
|
|
`${issuer}/.well-known/openid-configuration` |
|
|
`${issuer}/.well-known/openid-configuration` |
|
|
@ -160,34 +90,27 @@ ${formattedVariables} |
|
|
userinfo_endpoint: string; |
|
|
userinfo_endpoint: string; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
options = { |
|
|
// Manual URLs take priority over discovered ones
|
|
|
issuer, |
|
|
authorizationURL = |
|
|
scope, |
|
|
manualAuthorizationUrl || config.authorization_endpoint; |
|
|
authorizationURL: config.authorization_endpoint, |
|
|
tokenURL = manualTokenUrl || config.token_endpoint; |
|
|
callbackURL: callbackUrl, |
|
|
userInfoURL = manualUserInfoUrl || config.userinfo_endpoint; |
|
|
clientID: configurationService.get('OIDC_CLIENT_ID'), |
|
|
|
|
|
clientSecret: configurationService.get('OIDC_CLIENT_SECRET'), |
|
|
|
|
|
tokenURL: config.token_endpoint, |
|
|
|
|
|
userInfoURL: config.userinfo_endpoint |
|
|
|
|
|
}; |
|
|
|
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
Logger.error(error, 'OidcStrategy'); |
|
|
Logger.error(error, 'OidcStrategy'); |
|
|
throw new Error('Failed to fetch OIDC configuration from issuer'); |
|
|
throw new Error('Failed to fetch OIDC configuration from issuer'); |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} |
|
|
options = { |
|
|
|
|
|
|
|
|
const options: StrategyOptions = { |
|
|
|
|
|
issuer, |
|
|
scope, |
|
|
scope, |
|
|
authorizationURL: configurationService.get( |
|
|
authorizationURL, |
|
|
'OIDC_AUTHORIZATION_URL' |
|
|
|
|
|
), |
|
|
|
|
|
callbackURL: callbackUrl, |
|
|
callbackURL: callbackUrl, |
|
|
clientID: configurationService.get('OIDC_CLIENT_ID'), |
|
|
clientID: configurationService.get('OIDC_CLIENT_ID'), |
|
|
clientSecret: configurationService.get('OIDC_CLIENT_SECRET'), |
|
|
clientSecret: configurationService.get('OIDC_CLIENT_SECRET'), |
|
|
issuer: configurationService.get('OIDC_ISSUER'), |
|
|
tokenURL, |
|
|
tokenURL: configurationService.get('OIDC_TOKEN_URL'), |
|
|
userInfoURL |
|
|
userInfoURL: configurationService.get('OIDC_USER_INFO_URL') |
|
|
|
|
|
}; |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return new OidcStrategy(authService, options); |
|
|
return new OidcStrategy(authService, options); |
|
|
} |
|
|
} |
|
|
|