diff --git a/.env.example b/.env.example index e0ebf42..0fababc 100644 --- a/.env.example +++ b/.env.example @@ -50,7 +50,10 @@ KNOWLEDGE_CACHE_TTL=300 OIDC_ISSUER=https://auth.example.com/application/o/mosaic-stack/ OIDC_CLIENT_ID=your-client-id-here OIDC_CLIENT_SECRET=your-client-secret-here -OIDC_REDIRECT_URI=http://localhost:3001/auth/callback +# Redirect URI must match what's configured in Authentik +# Development: http://localhost:3001/auth/callback/authentik +# Production: https://api.mosaicstack.dev/auth/callback/authentik +OIDC_REDIRECT_URI=http://localhost:3001/auth/callback/authentik # Authentik PostgreSQL Database AUTHENTIK_POSTGRES_USER=authentik diff --git a/apps/api/src/auth/auth.config.ts b/apps/api/src/auth/auth.config.ts index f89bd9b..8abefed 100644 --- a/apps/api/src/auth/auth.config.ts +++ b/apps/api/src/auth/auth.config.ts @@ -1,5 +1,6 @@ import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; +import { genericOAuth } from "better-auth/plugins"; import type { PrismaClient } from "@prisma/client"; export function createAuth(prisma: PrismaClient) { @@ -10,13 +11,28 @@ export function createAuth(prisma: PrismaClient) { emailAndPassword: { enabled: true, // Enable for now, can be disabled later }, + plugins: [ + genericOAuth({ + config: [ + { + providerId: "authentik", + clientId: process.env.OIDC_CLIENT_ID ?? "", + clientSecret: process.env.OIDC_CLIENT_SECRET ?? "", + discoveryUrl: `${process.env.OIDC_ISSUER ?? ""}.well-known/openid-configuration`, + scopes: ["openid", "profile", "email"], + }, + ], + }), + ], session: { expiresIn: 60 * 60 * 24, // 24 hours updateAge: 60 * 60 * 24, // 24 hours }, trustedOrigins: [ process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000", - "http://localhost:3001", // API origin + "http://localhost:3001", // API origin (dev) + "https://app.mosaicstack.dev", // Production web + "https://api.mosaicstack.dev", // Production API ], }); } diff --git a/apps/web/src/components/auth/LoginButton.test.tsx b/apps/web/src/components/auth/LoginButton.test.tsx index bed19f9..96fc93d 100644 --- a/apps/web/src/components/auth/LoginButton.test.tsx +++ b/apps/web/src/components/auth/LoginButton.test.tsx @@ -32,9 +32,7 @@ describe("LoginButton", (): void => { const button = screen.getByRole("button", { name: /sign in/i }); await user.click(button); - expect(mockLocation.assign).toHaveBeenCalledWith( - "http://localhost:3001/auth/callback/authentik" - ); + expect(mockLocation.assign).toHaveBeenCalledWith("http://localhost:3001/auth/signin/authentik"); }); it("should have proper styling", (): void => { diff --git a/apps/web/src/components/auth/LoginButton.tsx b/apps/web/src/components/auth/LoginButton.tsx index faff5d5..858dd62 100644 --- a/apps/web/src/components/auth/LoginButton.tsx +++ b/apps/web/src/components/auth/LoginButton.tsx @@ -8,7 +8,7 @@ export function LoginButton(): React.JSX.Element { const handleLogin = (): void => { // Redirect to the backend OIDC authentication endpoint // BetterAuth will handle the OIDC flow and redirect back to the callback - window.location.assign(`${API_URL}/auth/callback/authentik`); + window.location.assign(`${API_URL}/auth/signin/authentik`); }; return (