fix(#337): Validate OIDC configuration at startup, fail fast if missing
- Add OIDC_ENABLED environment variable to control OIDC authentication - Validate required OIDC env vars (OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET) are present when OIDC is enabled - Validate OIDC_ISSUER ends with trailing slash for correct discovery URL - Throw descriptive error at startup if configuration is invalid - Skip OIDC plugin registration when OIDC is disabled - Add comprehensive tests for validation logic (17 test cases) Refs #337 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,85 @@ import { prismaAdapter } from "better-auth/adapters/prisma";
|
||||
import { genericOAuth } from "better-auth/plugins";
|
||||
import type { PrismaClient } from "@prisma/client";
|
||||
|
||||
/**
|
||||
* Required OIDC environment variables when OIDC is enabled
|
||||
*/
|
||||
const REQUIRED_OIDC_ENV_VARS = ["OIDC_ISSUER", "OIDC_CLIENT_ID", "OIDC_CLIENT_SECRET"] as const;
|
||||
|
||||
/**
|
||||
* Check if OIDC authentication is enabled via environment variable
|
||||
*/
|
||||
export function isOidcEnabled(): boolean {
|
||||
const enabled = process.env.OIDC_ENABLED;
|
||||
return enabled === "true" || enabled === "1";
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates OIDC configuration at startup.
|
||||
* Throws an error if OIDC is enabled but required environment variables are missing.
|
||||
*
|
||||
* @throws Error if OIDC is enabled but required vars are missing or empty
|
||||
*/
|
||||
export function validateOidcConfig(): void {
|
||||
if (!isOidcEnabled()) {
|
||||
// OIDC is disabled, no validation needed
|
||||
return;
|
||||
}
|
||||
|
||||
const missingVars: string[] = [];
|
||||
|
||||
for (const envVar of REQUIRED_OIDC_ENV_VARS) {
|
||||
const value = process.env[envVar];
|
||||
if (!value || value.trim() === "") {
|
||||
missingVars.push(envVar);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingVars.length > 0) {
|
||||
throw new Error(
|
||||
`OIDC authentication is enabled (OIDC_ENABLED=true) but required environment variables are missing or empty: ${missingVars.join(", ")}. ` +
|
||||
`Either set these variables or disable OIDC by setting OIDC_ENABLED=false.`
|
||||
);
|
||||
}
|
||||
|
||||
// Additional validation: OIDC_ISSUER should end with a trailing slash for proper discovery URL
|
||||
const issuer = process.env.OIDC_ISSUER;
|
||||
if (issuer && !issuer.endsWith("/")) {
|
||||
throw new Error(
|
||||
`OIDC_ISSUER must end with a trailing slash (/). Current value: "${issuer}". ` +
|
||||
`The discovery URL is constructed by appending ".well-known/openid-configuration" to the issuer.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OIDC plugins configuration.
|
||||
* Returns empty array if OIDC is disabled, otherwise returns configured OAuth plugin.
|
||||
*/
|
||||
function getOidcPlugins(): ReturnType<typeof genericOAuth>[] {
|
||||
if (!isOidcEnabled()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
genericOAuth({
|
||||
config: [
|
||||
{
|
||||
providerId: "authentik",
|
||||
clientId: process.env.OIDC_CLIENT_ID ?? "",
|
||||
clientSecret: process.env.OIDC_CLIENT_SECRET ?? "",
|
||||
discoveryUrl: `${process.env.OIDC_ISSUER ?? ""}.well-known/openid-configuration`,
|
||||
scopes: ["openid", "profile", "email"],
|
||||
},
|
||||
],
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
export function createAuth(prisma: PrismaClient) {
|
||||
// Validate OIDC configuration at startup - fail fast if misconfigured
|
||||
validateOidcConfig();
|
||||
|
||||
return betterAuth({
|
||||
database: prismaAdapter(prisma, {
|
||||
provider: "postgresql",
|
||||
@@ -11,19 +89,7 @@ export function createAuth(prisma: PrismaClient) {
|
||||
emailAndPassword: {
|
||||
enabled: true, // Enable for now, can be disabled later
|
||||
},
|
||||
plugins: [
|
||||
genericOAuth({
|
||||
config: [
|
||||
{
|
||||
providerId: "authentik",
|
||||
clientId: process.env.OIDC_CLIENT_ID ?? "",
|
||||
clientSecret: process.env.OIDC_CLIENT_SECRET ?? "",
|
||||
discoveryUrl: `${process.env.OIDC_ISSUER ?? ""}.well-known/openid-configuration`,
|
||||
scopes: ["openid", "profile", "email"],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
plugins: [...getOidcPlugins()],
|
||||
session: {
|
||||
expiresIn: 60 * 60 * 24, // 24 hours
|
||||
updateAge: 60 * 60 * 24, // 24 hours
|
||||
|
||||
Reference in New Issue
Block a user