keycloak improve flow 2

This commit is contained in:
alma 2026-01-02 14:12:15 +01:00
parent 9462fc6799
commit 88f0e946fd
3 changed files with 43 additions and 4 deletions

View File

@ -237,6 +237,17 @@ export const authOptions: NextAuthOptions = {
};
}
// If refresh failed with invalid_grant (token not active), also clear tokens
if (refreshedToken.error === "RefreshAccessTokenError" && !refreshedToken.accessToken) {
console.log("Refresh token invalid, clearing session to force re-authentication");
return {
...refreshedToken,
accessToken: undefined,
refreshToken: undefined,
idToken: undefined,
};
}
return refreshedToken;
},
async session({ session, token }) {

View File

@ -48,6 +48,21 @@ export async function GET(request: NextRequest) {
if (!response.ok) {
const error = await response.json().catch(() => ({}));
console.error('Failed to refresh Keycloak session:', error);
// If token is invalid (user logged out from Keycloak), return specific error
if (error.error === 'invalid_grant' ||
error.error_description?.includes('Token is not active') ||
error.error_description?.includes('Session not active')) {
return NextResponse.json(
{
error: 'SessionInvalidated',
message: 'Keycloak session was invalidated. Please sign in again.',
details: error
},
{ status: 401 }
);
}
return NextResponse.json(
{ error: 'Failed to refresh Keycloak session', details: error },
{ status: response.status }

View File

@ -15,11 +15,13 @@ export function ResponsiveIframe({ src, className = '', allow, style }: Responsi
const { data: session } = useSession();
const [isRefreshing, setIsRefreshing] = useState(false);
const [iframeSrc, setIframeSrc] = useState<string>('');
const [hasTriedRefresh, setHasTriedRefresh] = useState(false);
// Refresh NextAuth session (which will also refresh Keycloak tokens) before loading iframe
useEffect(() => {
if (!session || !src || isRefreshing) {
if (src && !isRefreshing) {
// If no session or no src, or already tried refresh, just set src
if (!session || !src || hasTriedRefresh) {
if (src) {
setIframeSrc(src);
}
return;
@ -27,6 +29,7 @@ export function ResponsiveIframe({ src, className = '', allow, style }: Responsi
const refreshSession = async () => {
setIsRefreshing(true);
setHasTriedRefresh(true);
try {
// Call our API to refresh the Keycloak session
@ -39,19 +42,29 @@ export function ResponsiveIframe({ src, className = '', allow, style }: Responsi
if (response.ok) {
console.log('Session refreshed before loading iframe');
} else {
const errorData = await response.json().catch(() => ({}));
// If session was invalidated, redirect to sign-in
if (response.status === 401 && errorData.error === 'SessionInvalidated') {
console.warn('Keycloak session invalidated, redirecting to sign-in');
// Sign out from NextAuth and redirect
window.location.href = '/signin';
return;
}
console.warn('Failed to refresh session, iframe may require login');
}
} catch (error) {
console.error('Error refreshing session:', error);
} finally {
// Set iframe src after attempting refresh
// Always set iframe src after attempting refresh (or on error)
setIframeSrc(src);
setIsRefreshing(false);
}
};
refreshSession();
}, [session, src, isRefreshing]);
}, [session, src, hasTriedRefresh]);
useEffect(() => {
const iframe = iframeRef.current;