Centralize authentication logic and integrate token refresh mechanism
- Introduce `AuthWrapper` component for streamlined session-based layouts and authentication handling. - Add new utilities (`tokenUtils.ts`) for JWT decoding, token expiration checks, and refresh operations via Keycloak. - Refactor `serverCall` and `authOptions` to use centralized token refresh logic, removing redundant implementations. - Implement `ClientSessionProvider` for consistent session management across the client application. - Simplify `RootLayout` by delegating authentication enforcement to `AuthWrapper`.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import KeycloakProvider from "next-auth/providers/keycloak";
|
||||
import type {NextAuthOptions} from "next-auth";
|
||||
import {getAccessToken} from "@/lib/api/auth/tokenUtils";
|
||||
|
||||
interface TypedJWT {
|
||||
access_token?: string;
|
||||
@@ -20,9 +21,9 @@ if (!KEYCLOAK_CLIENT_SECRET) throw new Error("Missing KEYCLOAK_CLIENT_SECRET");
|
||||
if (!KEYCLOAK_ISSUER) throw new Error("Missing KEYCLOAK_ISSUER");
|
||||
if (!NEXTAUTH_SECRET) throw new Error("Missing NEXTAUTH_SECRET");
|
||||
|
||||
console.log("[auth] Using Keycloak provider:");
|
||||
console.log(" - Client ID:", KEYCLOAK_CLIENT_ID);
|
||||
console.log(" - Issuer:", KEYCLOAK_ISSUER);
|
||||
// console.log("[auth] Using Keycloak provider:");
|
||||
// console.log(" - Client ID:", KEYCLOAK_CLIENT_ID);
|
||||
// console.log(" - Issuer:", KEYCLOAK_ISSUER);
|
||||
|
||||
async function isTokenValid(token: string): Promise<boolean> {
|
||||
try {
|
||||
@@ -60,22 +61,39 @@ export const authOptions: NextAuthOptions = {
|
||||
return token;
|
||||
}
|
||||
|
||||
const {access_token} = token as TypedJWT;
|
||||
const {access_token, refresh_token} = token as TypedJWT;
|
||||
if (access_token) {
|
||||
const valid = await isTokenValid(access_token);
|
||||
if (!valid) {
|
||||
console.warn("[auth] Access token invalid — clearing session");
|
||||
// Use centralized getAccessToken function
|
||||
const tokenResult = await getAccessToken(access_token, refresh_token);
|
||||
|
||||
if (tokenResult.accessToken) {
|
||||
token.access_token = tokenResult.accessToken;
|
||||
if (tokenResult.refreshToken) {
|
||||
token.refresh_token = tokenResult.refreshToken;
|
||||
}
|
||||
|
||||
if (tokenResult.refreshed) {
|
||||
console.log("[auth] Token refreshed successfully in JWT callback");
|
||||
return token;
|
||||
}
|
||||
|
||||
// If token wasn't refreshed, fall back to network validation
|
||||
const valid = await isTokenValid(tokenResult.accessToken);
|
||||
if (!valid) {
|
||||
console.warn("[auth] Access token invalid — clearing session");
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
console.warn("[auth] No valid access token available — clearing session");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
console.log("[auth] JWT callback: reusing existing token");
|
||||
return token;
|
||||
},
|
||||
|
||||
async session({session, token}) {
|
||||
const {access_token, refresh_token} = token as TypedJWT;
|
||||
console.log("[auth] Session callback: enriching session with tokens");
|
||||
return {
|
||||
...session,
|
||||
accessToken: access_token,
|
||||
|
||||
Reference in New Issue
Block a user