From bf0bee9c288f48091b921f6e2389bc282907c7a9 Mon Sep 17 00:00:00 2001 From: Vadym Samoilenko Date: Wed, 15 Apr 2026 15:54:37 +0100 Subject: [PATCH] Fix SSO: use /api/auth (no basePath) as OAuth redirect_uri MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit next-auth v5 beta.30 cannot reliably pass the /hp-prod-tracker prefix through OAuth redirect_uri — redirectProxyUrl is silently ignored. Instead: AUTH_URL=https://…/api/auth (matches basePath exactly), Auth.js sends consistent redirect_uri in both authorization and token exchange, Apache proxies /api/auth → :3001 before the OliVAS /api/ rule. Azure must have https://optical-dev.oliver.solutions/api/auth/callback/microsoft-entra-id registered. Server .env: AUTH_URL=https://optical-dev.oliver.solutions/api/auth Co-Authored-By: Claude Sonnet 4.6 --- apache/hp-prod-tracker.conf | 5 +++++ src/lib/auth.ts | 29 +++++++++-------------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/apache/hp-prod-tracker.conf b/apache/hp-prod-tracker.conf index 57de048..86013f2 100644 --- a/apache/hp-prod-tracker.conf +++ b/apache/hp-prod-tracker.conf @@ -12,6 +12,11 @@ RewriteCond %{HTTP:Upgrade} websocket [NC] RewriteCond %{HTTP:Connection} upgrade [NC] RewriteRule ^/hp-prod-tracker/(.*) ws://127.0.0.1:3001/hp-prod-tracker/$1 [P,L] +# OAuth callback — must be defined BEFORE the global /api/ → OliVAS rule. +# Auth.js uses /api/auth/* without the Next.js basePath in redirect_uri. +ProxyPass /api/auth http://127.0.0.1:3001/api/auth +ProxyPassReverse /api/auth http://127.0.0.1:3001/api/auth + # Chat + AI endpoints: long timeout for streaming responses ProxyPass /hp-prod-tracker/api/chat http://127.0.0.1:3001/hp-prod-tracker/api/chat timeout=300 ProxyPassReverse /hp-prod-tracker/api/chat http://127.0.0.1:3001/hp-prod-tracker/api/chat diff --git a/src/lib/auth.ts b/src/lib/auth.ts index fb3ba6e..9ca9d1f 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -4,22 +4,18 @@ import { PrismaAdapter } from "@auth/prisma-adapter"; import { prisma } from "@/lib/prisma"; import type { Role } from "@/generated/prisma/client"; -// AUTH_URL must be the full auth endpoint URL including basePath, e.g.: -// https://optical-dev.oliver.solutions/hp-prod-tracker/api/auth +// AUTH_URL = https://optical-dev.oliver.solutions/api/auth // -// next-auth v5 beta ignores redirectProxyUrl for the authorization request -// (strips pathname from AUTH_URL) but DOES use it for the token exchange. -// We fix the authorization request via authorization.params.redirect_uri -// and restore redirectProxyUrl so token exchange uses the same URI. -const authUrl = process.env.AUTH_URL; // e.g. https://…/hp-prod-tracker/api/auth -const explicitRedirectUri = authUrl - ? `${authUrl}/callback/microsoft-entra-id` - : undefined; +// We intentionally use the /api/auth path WITHOUT the Next.js /hp-prod-tracker +// basePath. next-auth v5 beta cannot reliably pass the app basePath through the +// OAuth redirect_uri — redirectProxyUrl is silently ignored in beta.30. +// +// Instead: AUTH_URL matches basePath exactly (/api/auth) so there is no +// env-url-basepath-mismatch, Auth.js sends a consistent redirect_uri in both +// the authorization and token exchange requests, and Apache proxies /api/auth +// directly to the container (see apache/hp-prod-tracker.conf). export const { handlers, auth, signIn, signOut } = NextAuth({ - // Explicit basePath prevents AUTH_URL's pathname from overriding it. - // Next.js strips the /hp-prod-tracker prefix before Auth.js sees the request, - // so internally routes must match /api/auth/*. basePath: "/api/auth", adapter: PrismaAdapter(prisma), providers: [ @@ -30,13 +26,6 @@ export const { handlers, auth, signIn, signOut } = NextAuth({ // Safe for Entra ID — Microsoft verifies organizational emails. // Required to link SSO accounts to pre-seeded User records by email match. allowDangerousEmailAccountLinking: true, - // authorization.params: fixes redirect_uri in the authorization request - // redirectProxyUrl: fixes redirect_uri in the token exchange request - // Both are needed — beta.30 ignores redirectProxyUrl for authorization. - ...(explicitRedirectUri && { - authorization: { params: { redirect_uri: explicitRedirectUri } }, - }), - redirectProxyUrl: authUrl, }), ], session: {