fix: get env variable dynamically to avoid secret leakage

This commit is contained in:
Raul Lugo
2026-01-28 02:17:06 +01:00
parent 4abf69844a
commit 4e33401387
10 changed files with 238 additions and 58 deletions

View File

@@ -1,43 +1,60 @@
import type { AstroIntegration } from "astro";
import { fileURLToPath } from "node:url";
export type EnvString = {
env: string;
fallback?: string;
};
export type RequiredSecret = {
env: string;
};
export type OidcIntegrationOptions = {
issuer: string;
clientId: string;
issuer: EnvString;
clientId: EnvString;
scopes?: string; // default "openid email profile"
routes?: { login?: string; callback?: string; logout?: string }; // defaults: /login, /oidc/callback, /logout
redirectUri?: { mode: "infer-from-request" } | { absolute: string };
cookie?: {
cookie: {
name?: string;
sameSite?: "Lax" | "Strict" | "None";
secure?: boolean;
domain?: string;
path?: string;
signingSecret: string;
signingSecret: RequiredSecret;
maxAgeSec?: number;
};
protected?: string[]; // patterns to guard, e.g., ["/app/*", "/me"]
};
export type OidcResolvedOptions = Required<
Pick<OidcIntegrationOptions, "issuer" | "clientId">
> & {
export type OidcInjectedOptions = {
issuerEnv: string;
issuerFallback?: string;
clientIdEnv: string;
clientIdFallback?: string;
scopes: string;
routes: { login: string; callback: string; logout: string };
redirectUri: { mode: "infer-from-request" } | { absolute: string };
cookie: Required<
Pick<
NonNullable<OidcIntegrationOptions["cookie"]>,
"name" | "sameSite" | "secure" | "path" | "signingSecret"
>
> & {
cookie: {
name: string;
sameSite: "Lax" | "Strict" | "None";
secure: boolean;
path: string;
signingSecretEnv: string;
domain?: string;
maxAgeSec?: number;
};
protected: string[];
};
function resolveOptions(user: OidcIntegrationOptions): OidcResolvedOptions {
function requireEnvName(name: string, label: string): string {
if (typeof name !== "string" || name.trim().length === 0)
throw new Error(`@resuely/astro-oidc-rp: ${label} must be a non-empty string`);
return name;
}
function resolveOptions(user: OidcIntegrationOptions): OidcInjectedOptions {
const routes = {
login: user.routes?.login ?? "/login",
callback: user.routes?.callback ?? "/oidc/callback",
@@ -48,19 +65,23 @@ function resolveOptions(user: OidcIntegrationOptions): OidcResolvedOptions {
sameSite: user.cookie?.sameSite ?? "Lax",
secure: user.cookie?.secure ?? true,
path: user.cookie?.path ?? "/",
signingSecret: user.cookie?.signingSecret ?? "",
signingSecretEnv: requireEnvName(
user.cookie?.signingSecret?.env,
"cookie.signingSecret.env",
),
domain: user.cookie?.domain,
maxAgeSec: user.cookie?.maxAgeSec,
};
if (!user.issuer)
throw new Error("@resuely/astro-oidc-rp: 'issuer' is required");
if (!user.clientId)
throw new Error("@resuely/astro-oidc-rp: 'clientId' is required");
if (!cookie.signingSecret)
throw new Error("@resuely/astro-oidc-rp: cookie.signingSecret is required");
return {
issuer: user.issuer,
clientId: user.clientId,
issuerEnv: requireEnvName(user.issuer.env, "issuer.env"),
issuerFallback:
typeof user.issuer.fallback === "string" ? user.issuer.fallback : undefined,
clientIdEnv: requireEnvName(user.clientId.env, "clientId.env"),
clientIdFallback:
typeof user.clientId.fallback === "string"
? user.clientId.fallback
: undefined,
scopes: user.scopes ?? "openid email profile",
routes,
redirectUri: user.redirectUri ?? { mode: "infer-from-request" },
@@ -109,4 +130,3 @@ export default function resuelyOidc(
},
};
}