820a5240d1
- Fix Dockerfiles to copy pnpm-lock.yaml (frozen-lockfile compliance) - Add migrate target to API Dockerfile using builder stage - Add migrate service to docker-compose that runs before API starts - Add AUTH_DISABLED env var bypass to auth middleware for dev/Docker - Proxy /api/ from nginx to API container (no CORS needed) - Include initial Drizzle migration (0000_colossal_colossus.sql) - Add .env.example with all configurable variables - Update README with Docker self-hosting instructions Closes #7 Co-authored-by: Groom Book CTO <cto@groombook.app> Co-authored-by: Paperclip <noreply@paperclip.ing>
52 lines
1.3 KiB
TypeScript
52 lines
1.3 KiB
TypeScript
import type { MiddlewareHandler } from "hono";
|
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
|
|
// Authentik OIDC configuration — loaded from env at startup
|
|
const OIDC_ISSUER = process.env.OIDC_ISSUER;
|
|
const OIDC_AUDIENCE = process.env.OIDC_AUDIENCE;
|
|
|
|
let jwks: ReturnType<typeof createRemoteJWKSet> | null = null;
|
|
|
|
function getJwks() {
|
|
if (!OIDC_ISSUER) throw new Error("OIDC_ISSUER is not set");
|
|
if (!jwks) {
|
|
jwks = createRemoteJWKSet(
|
|
new URL(`${OIDC_ISSUER}/application/o/groombook/jwks/`)
|
|
);
|
|
}
|
|
return jwks;
|
|
}
|
|
|
|
export interface JwtPayload {
|
|
sub: string;
|
|
email?: string;
|
|
name?: string;
|
|
}
|
|
|
|
export const authMiddleware: MiddlewareHandler = async (c, next) => {
|
|
if (process.env.AUTH_DISABLED === "true") {
|
|
c.set("jwtPayload", { sub: "dev-user" } as JwtPayload);
|
|
await next();
|
|
return;
|
|
}
|
|
|
|
const authorization = c.req.header("Authorization");
|
|
if (!authorization?.startsWith("Bearer ")) {
|
|
return c.json({ error: "Unauthorized" }, 401);
|
|
}
|
|
|
|
const token = authorization.slice(7);
|
|
|
|
try {
|
|
const { payload } = await jwtVerify(token, getJwks(), {
|
|
issuer: OIDC_ISSUER,
|
|
audience: OIDC_AUDIENCE,
|
|
});
|
|
|
|
c.set("jwtPayload", payload as JwtPayload);
|
|
await next();
|
|
} catch {
|
|
return c.json({ error: "Invalid or expired token" }, 401);
|
|
}
|
|
};
|