6872342d8f
## Changes - Replace toNodeHandler with auth.handler(c.req.raw) sub-app mount for Hono compatibility - Add /api/auth/ path skip in authMiddleware and resolveStaffMiddleware - Add OIDC_INTERNAL_BASE env var for split-horizon (hairpin NAT) URL resolution - Replace render-time signIn.social() with LoginPage component (fixes redirect loop) - Change auth-client baseURL to relative (empty string) for deployed environments - Add POST /api/portal/appointments/:id/reschedule endpoint with session auth - Add RescheduleFlow modal, PetForm component, and wire Dashboard/Appointments UI ## CTO Note Auth fix is P0-critical. Portal mock data (UAT blocker) predates this PR and is tracked separately in GRO-218. Co-Authored-By: Paperclip <noreply@paperclip.ing>
62 lines
2.2 KiB
TypeScript
62 lines
2.2 KiB
TypeScript
import { betterAuth } from "better-auth";
|
|
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
import { genericOAuth } from "better-auth/plugins";
|
|
import { getDb } from "@groombook/db";
|
|
|
|
const OIDC_ISSUER = process.env.OIDC_ISSUER;
|
|
const OIDC_INTERNAL_BASE = process.env.OIDC_INTERNAL_BASE; // e.g. http://authentik-server.auth.svc.cluster.local
|
|
const OIDC_CLIENT_ID = process.env.OIDC_CLIENT_ID;
|
|
const OIDC_CLIENT_SECRET = process.env.OIDC_CLIENT_SECRET;
|
|
const BETTER_AUTH_SECRET = process.env.BETTER_AUTH_SECRET;
|
|
const BETTER_AUTH_URL = process.env.BETTER_AUTH_URL ?? "http://localhost:3000";
|
|
|
|
if (!BETTER_AUTH_SECRET && process.env.AUTH_DISABLED !== "true") {
|
|
throw new Error(
|
|
"[FATAL] BETTER_AUTH_SECRET environment variable is required when auth is enabled"
|
|
);
|
|
}
|
|
|
|
export const auth = betterAuth({
|
|
database: drizzleAdapter(getDb(), {
|
|
provider: "pg",
|
|
}),
|
|
secret: BETTER_AUTH_SECRET,
|
|
baseURL: BETTER_AUTH_URL,
|
|
plugins: [
|
|
genericOAuth({
|
|
config: [
|
|
{
|
|
providerId: "authentik",
|
|
clientId: OIDC_CLIENT_ID ?? "",
|
|
clientSecret: OIDC_CLIENT_SECRET ?? "",
|
|
// When OIDC_INTERNAL_BASE is set, use explicit URLs to avoid hairpin NAT:
|
|
// - authorizationUrl: external (browser redirect, no server-side fetch)
|
|
// - tokenUrl/userInfoUrl: internal (server-to-server, avoids hairpin)
|
|
// When not set, fall back to discoveryUrl for local dev.
|
|
...(OIDC_INTERNAL_BASE
|
|
? {
|
|
authorizationUrl: `${new URL(OIDC_ISSUER!).origin}/application/o/authorize/`,
|
|
tokenUrl: `${OIDC_INTERNAL_BASE}/application/o/token/`,
|
|
userInfoUrl: `${OIDC_INTERNAL_BASE}/application/o/userinfo/`,
|
|
}
|
|
: {
|
|
discoveryUrl: OIDC_ISSUER
|
|
? `${OIDC_ISSUER}/.well-known/openid-configuration`
|
|
: undefined,
|
|
}),
|
|
scopes: ["openid", "profile", "email"],
|
|
},
|
|
],
|
|
}),
|
|
],
|
|
session: {
|
|
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
|
updateAge: 60 * 60 * 24, // 1 day
|
|
cookieCache: {
|
|
enabled: true,
|
|
maxAge: 5 * 60, // 5 minutes
|
|
},
|
|
},
|
|
trustedOrigins: [process.env.CORS_ORIGIN ?? "http://localhost:5173"],
|
|
});
|