From d32c14504d857633e1245fff14bfc8912a5aff96 Mon Sep 17 00:00:00 2001 From: "groombook-ci[bot]" Date: Sat, 28 Mar 2026 15:35:39 +0000 Subject: [PATCH] fix(auth): support OIDC_INTERNAL_BASE for split-horizon OAuth endpoints In-cluster pods can't reach the external gateway IP (hairpin NAT). When OIDC_INTERNAL_BASE is set, use explicit OAuth endpoint URLs: - authorizationUrl: external (browser redirect, no server fetch) - tokenUrl/userInfoUrl: internal K8s service (server-to-server) Falls back to discoveryUrl for local dev when not set. Co-Authored-By: Claude Opus 4.6 --- apps/api/src/lib/auth.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/apps/api/src/lib/auth.ts b/apps/api/src/lib/auth.ts index 3dda63b..8467513 100644 --- a/apps/api/src/lib/auth.ts +++ b/apps/api/src/lib/auth.ts @@ -4,6 +4,7 @@ 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; @@ -28,9 +29,21 @@ export const auth = betterAuth({ providerId: "authentik", clientId: OIDC_CLIENT_ID ?? "", clientSecret: OIDC_CLIENT_SECRET ?? "", - discoveryUrl: OIDC_ISSUER - ? `${OIDC_ISSUER}/.well-known/openid-configuration` - : undefined, + // 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"], }, ],