first commit

This commit is contained in:
Raul Lugo
2026-01-22 12:31:05 +01:00
commit 261be4ea0e
16 changed files with 7031 additions and 0 deletions

112
src/index.ts Normal file
View File

@@ -0,0 +1,112 @@
import type { AstroIntegration } from "astro";
import { fileURLToPath } from "node:url";
export type OidcIntegrationOptions = {
issuer: string;
clientId: string;
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?: {
name?: string;
sameSite?: "Lax" | "Strict" | "None";
secure?: boolean;
domain?: string;
path?: string;
signingSecret: string;
maxAgeSec?: number;
};
protected?: string[]; // patterns to guard, e.g., ["/app/*", "/me"]
};
export type OidcResolvedOptions = Required<
Pick<OidcIntegrationOptions, "issuer" | "clientId">
> & {
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"
>
> & {
domain?: string;
maxAgeSec?: number;
};
protected: string[];
};
function resolveOptions(user: OidcIntegrationOptions): OidcResolvedOptions {
const routes = {
login: user.routes?.login ?? "/login",
callback: user.routes?.callback ?? "/oidc/callback",
logout: user.routes?.logout ?? "/logout",
};
const cookie = {
name: user.cookie?.name ?? "oidc_session",
sameSite: user.cookie?.sameSite ?? "Lax",
secure: user.cookie?.secure ?? true,
path: user.cookie?.path ?? "/",
signingSecret: user.cookie?.signingSecret ?? "",
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,
scopes: user.scopes ?? "openid email profile",
routes,
redirectUri: user.redirectUri ?? { mode: "infer-from-request" },
cookie,
protected: user.protected ?? [],
};
}
export default function resuelyOidc(
options: OidcIntegrationOptions,
): AstroIntegration {
const resolved = resolveOptions(options);
return {
name: "@resuely/astro-oidc-rp",
hooks: {
"astro:config:setup": ({ injectRoute, addMiddleware, updateConfig }) => {
// Provide runtime options to middleware/routes via Vite define replacement
updateConfig({
vite: {
define: {
__RESUELY_OIDC_OPTIONS: JSON.stringify(resolved),
},
},
});
// Add middleware (run early to populate locals and guard protected patterns)
addMiddleware({
entrypoint: fileURLToPath(new URL("./middleware.js", import.meta.url)),
order: "pre",
});
// Inject routes
injectRoute({
pattern: resolved.routes.login,
entrypoint: fileURLToPath(new URL("./routes/login.js", import.meta.url)),
});
injectRoute({
pattern: resolved.routes.callback,
entrypoint: fileURLToPath(new URL("./routes/callback.js", import.meta.url)),
});
injectRoute({
pattern: resolved.routes.logout,
entrypoint: fileURLToPath(new URL("./routes/logout.js", import.meta.url)),
});
},
},
};
}