Files
work-club-manager/frontend/src/auth/auth.ts
T

63 lines
1.9 KiB
TypeScript
Raw Normal View History

import NextAuth from "next-auth"
import KeycloakProvider from "next-auth/providers/keycloak"
declare module "next-auth" {
interface Session {
user: {
id: string
name?: string | null
email?: string | null
image?: string | null
clubs?: Record<string, string>
}
accessToken?: string
}
interface JWT {
clubs?: Record<string, string>
accessToken?: string
}
}
// In Docker, the Next.js server reaches Keycloak via internal hostname
// (keycloak:8080) but the browser uses localhost:8080. Explicit endpoint
// URLs bypass OIDC discovery, avoiding issuer mismatch validation errors.
const issuerPublic = process.env.KEYCLOAK_ISSUER!
const issuerInternal = process.env.KEYCLOAK_ISSUER_INTERNAL || issuerPublic
const oidcPublic = `${issuerPublic}/protocol/openid-connect`
const oidcInternal = `${issuerInternal}/protocol/openid-connect`
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
KeycloakProvider({
clientId: process.env.KEYCLOAK_CLIENT_ID!,
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!,
issuer: issuerPublic,
authorization: {
url: `${oidcPublic}/auth`,
params: { scope: "openid email profile" },
},
token: `${oidcInternal}/token`,
userinfo: `${oidcInternal}/userinfo`,
})
],
callbacks: {
async jwt({ token, account }) {
if (account) {
// Add clubs claim from Keycloak access token
token.clubs = (account as Record<string, unknown>).clubs as Record<string, string> || {}
token.accessToken = account.access_token
}
return token
},
async session({ session, token }) {
// Expose clubs to client
if (session.user) {
session.user.clubs = token.clubs as Record<string, string> | undefined
}
session.accessToken = token.accessToken as string | undefined
return session
}
}
})