diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts index dd25a2a..5e1ea09 100644 --- a/app/api/auth/[...nextauth]/route.ts +++ b/app/api/auth/[...nextauth]/route.ts @@ -1,7 +1,44 @@ import NextAuth from "next-auth"; import { authOptions } from "@/lib/auth"; -const handler = NextAuth(authOptions); +const handler = NextAuth({ + ...authOptions, + debug: true, // Enable debug logging + callbacks: { + ...authOptions.callbacks, + async redirect({ url, baseUrl }) { + console.log('Redirect callback:', { url, baseUrl }); + // Allows relative callback URLs + if (url.startsWith("/")) return `${baseUrl}${url}`; + // Allows callback URLs on the same origin + else if (new URL(url).origin === baseUrl) return url; + return baseUrl; + }, + async session({ session, token }) { + console.log('Session callback:', { + sessionBefore: session, + token: { ...token, refreshToken: '[REDACTED]' } + }); + + if (token.error) { + console.error('Token error:', token.error); + throw new Error('RefreshAccessTokenError'); + } + + session.user.id = token.id; + session.user.email = token.email; + session.user.name = token.name; + session.user.role = token.role; + + console.log('Session after:', { + sessionAfter: { ...session, user: { ...session.user, id: '[REDACTED]' } } + }); + + return session; + }, + }, +}); + export { handler as GET, handler as POST }; interface JWT { diff --git a/lib/auth.ts b/lib/auth.ts index e2b7261..01a9587 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -43,8 +43,15 @@ export const authOptions: NextAuthOptions = { clientId: process.env.KEYCLOAK_CLIENT_ID!, clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!, issuer: process.env.KEYCLOAK_ISSUER, + authorization: { + params: { + scope: 'openid email profile', + response_type: 'code', + } + }, }), ], + debug: true, session: { strategy: 'jwt', maxAge: 30 * 24 * 60 * 60, // 30 days @@ -55,11 +62,19 @@ export const authOptions: NextAuthOptions = { }, callbacks: { async jwt({ token, account, profile }) { + console.log('JWT callback:', { + tokenBefore: { ...token, refreshToken: token.refreshToken ? '[REDACTED]' : undefined }, + account: account ? { ...account, refresh_token: '[REDACTED]' } : null, + profile + }); + if (account && profile) { if (!profile.sub) { + console.error('No user ID (sub) provided by Keycloak'); throw new Error('No user ID (sub) provided by Keycloak'); } if (!account.access_token || !account.refresh_token || !account.expires_at) { + console.error('Missing required token fields from Keycloak'); throw new Error('Missing required token fields from Keycloak'); } token.id = profile.sub; @@ -69,6 +84,10 @@ export const authOptions: NextAuthOptions = { token.accessToken = account.access_token; token.refreshToken = account.refresh_token; token.accessTokenExpires = account.expires_at * 1000; + + console.log('JWT token updated:', { + tokenAfter: { ...token, refreshToken: '[REDACTED]' } + }); } // Return previous token if not expired @@ -78,10 +97,12 @@ export const authOptions: NextAuthOptions = { // Token expired, try to refresh if (!token.refreshToken) { + console.error('No refresh token available'); throw new Error('No refresh token available'); } try { + console.log('Attempting to refresh token...'); const response = await fetch( `${process.env.KEYCLOAK_BASE_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`, { @@ -93,7 +114,7 @@ export const authOptions: NextAuthOptions = { grant_type: 'refresh_token', client_id: process.env.KEYCLOAK_CLIENT_ID!, client_secret: process.env.KEYCLOAK_CLIENT_SECRET!, - refresh_token: token.refreshToken as string, + refresh_token: token.refreshToken, }), } ); @@ -101,9 +122,11 @@ export const authOptions: NextAuthOptions = { const tokens = await response.json(); if (!response.ok) { + console.error('Token refresh failed:', tokens); throw new Error('RefreshAccessTokenError'); } + console.log('Token refreshed successfully'); return { ...token, accessToken: tokens.access_token, @@ -111,6 +134,7 @@ export const authOptions: NextAuthOptions = { accessTokenExpires: Date.now() + tokens.expires_in * 1000, }; } catch (error) { + console.error('Error refreshing token:', error); return { ...token, error: 'RefreshAccessTokenError',