correction

This commit is contained in:
alma 2026-01-04 01:05:28 +01:00
parent 517a5e897b
commit 1238deb36b
2 changed files with 15 additions and 8 deletions

File diff suppressed because one or more lines are too long

View File

@ -194,6 +194,7 @@ export const authOptions: NextAuthOptions = {
}, },
callbacks: { callbacks: {
async jwt({ token, account, profile }) { async jwt({ token, account, profile }) {
// Initial sign-in: account and profile are present
if (account && profile) { if (account && profile) {
const keycloakProfile = profile as KeycloakProfile; const keycloakProfile = profile as KeycloakProfile;
const roles = keycloakProfile.realm_access?.roles || []; const roles = keycloakProfile.realm_access?.roles || [];
@ -204,13 +205,20 @@ export const authOptions: NextAuthOptions = {
token.accessToken = account.access_token ?? ''; token.accessToken = account.access_token ?? '';
token.refreshToken = account.refresh_token ?? ''; token.refreshToken = account.refresh_token ?? '';
token.idToken = account.id_token ?? ''; token.idToken = account.id_token ?? '';
token.accessTokenExpires = account.expires_at ?? 0; // expires_at from Keycloak is in seconds since epoch, convert to milliseconds
token.accessTokenExpires = account.expires_at ? account.expires_at * 1000 : Date.now() + 3600 * 1000;
token.sub = keycloakProfile.sub; token.sub = keycloakProfile.sub;
token.role = cleanRoles; token.role = cleanRoles;
token.username = keycloakProfile.preferred_username ?? ''; token.username = keycloakProfile.preferred_username ?? '';
token.first_name = keycloakProfile.given_name ?? ''; token.first_name = keycloakProfile.given_name ?? '';
token.last_name = keycloakProfile.family_name ?? ''; token.last_name = keycloakProfile.family_name ?? '';
} else if (token.accessToken) {
// Return immediately on initial sign-in - don't try to refresh tokens we just received
return token;
}
// Subsequent requests: check existing token
if (token.accessToken) {
try { try {
const decoded = jwtDecode<DecodedToken>(token.accessToken as string); const decoded = jwtDecode<DecodedToken>(token.accessToken as string);
if (decoded.realm_access?.roles) { if (decoded.realm_access?.roles) {
@ -226,7 +234,7 @@ export const authOptions: NextAuthOptions = {
} }
// Check if token is expired and needs refresh // Check if token is expired and needs refresh
// accessTokenExpires is in milliseconds (Date.now() + expires_in * 1000) // accessTokenExpires is in milliseconds
const expiresAt = token.accessTokenExpires as number; const expiresAt = token.accessTokenExpires as number;
if (expiresAt && Date.now() < expiresAt) { if (expiresAt && Date.now() < expiresAt) {
// Token is still valid, return as-is // Token is still valid, return as-is
@ -273,7 +281,7 @@ export const authOptions: NextAuthOptions = {
return refreshedToken; return refreshedToken;
}, },
async session({ session, token }) { async session({ session, token }) {
// If session was invalidated or tokens are missing, return null to sign out // If session was invalidated or tokens are missing, throw error to trigger sign out
if (token.error === "SessionNotActive" || if (token.error === "SessionNotActive" ||
token.error === "NoRefreshToken" || token.error === "NoRefreshToken" ||
!token.accessToken || !token.accessToken ||
@ -284,11 +292,9 @@ export const authOptions: NextAuthOptions = {
hasRefreshToken: !!token.refreshToken hasRefreshToken: !!token.refreshToken
}); });
// Return null to make NextAuth treat user as unauthenticated // Throw error to make NextAuth treat user as unauthenticated
// This will trigger automatic redirect to sign-in page // This will trigger automatic redirect to sign-in page
// The client-side code will detect session invalidation by checking for throw new Error(token.error || "SessionInvalidated");
// session cookie existence when status is unauthenticated
return null as any;
} }
// For other errors, throw to trigger error handling // For other errors, throw to trigger error handling