diff --git a/front/.env.production b/front/.env.production index 8a6bc85..429080b 100644 --- a/front/.env.production +++ b/front/.env.production @@ -4,4 +4,5 @@ NEXTAUTH_SECRET=9eff5ad2f4b5ea744a34d9d8004cb5236f1931b26bf75f01a0a26203312fe1ec KEYCLOAK_CLIENT_ID=front KEYCLOAK_CLIENT_SECRET=Klsbm7hzyXscypXU0wUPPVBrttFPt6Pn KEYCLOAK_REALM=master -KEYCLOAK_ISSUER=http://172.16.32.141:8090/realms/master \ No newline at end of file +KEYCLOAK_ISSUER=http://172.16.32.141:8090/realms/master +KEYCLOAK_BASE_URL=http://172.16.32.141:8090 \ No newline at end of file diff --git a/front/app/api/auth/[...nextauth]/route.ts b/front/app/api/auth/[...nextauth]/route.ts index 1c1187d..356fb2d 100644 --- a/front/app/api/auth/[...nextauth]/route.ts +++ b/front/app/api/auth/[...nextauth]/route.ts @@ -22,23 +22,69 @@ export const authOptions: NextAuthOptions = { ], callbacks: { async jwt({ token, account, profile }) { - console.log("Token", token); - console.log("Account", account); - console.log("Profile", profile); // Au moment de la première connexion, sauvegarde de l'access token et du rôle dans le JWT if (account && profile) { token.accessToken = account.access_token; token.refreshToken = account.refresh_token; + token.accessTokenExpires = account.expires_at! * 1000; token.first_name = profile.given_name; token.last_name = profile.family_name; token.username = profile.preferred_username; token.role = profile.realm_roles; + return token; + } + + // Retourner le token précédent si le token d'accès n'a pas expiré + if (Date.now() < (token.accessTokenExpires as number)) { + return token; + } + + // Rafraîchir le token si expiré + try { + const response = await fetch( + `${process.env.KEYCLOAK_BASE_URL}/protocol/openid-connect/token`, + { + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + grant_type: "refresh_token", + client_id: process.env.KEYCLOAK_CLIENT_ID!, + client_secret: process.env.KEYCLOAK_CLIENT_SECRET!, + refresh_token: token.refreshToken as string, + }), + } + ); + + const refreshedTokens = await response.json(); + + if (!response.ok) { + throw refreshedTokens; + } + + return { + ...token, + accessToken: refreshedTokens.access_token, + refreshToken: refreshedTokens.refresh_token ?? token.refreshToken, + accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000, + }; + } catch (error) { + console.error("Erreur lors du rafraîchissement du token:", error); + return { + ...token, + error: "RefreshAccessTokenError", + }; } - return token; }, async session({ session, token }) { + if (token.error) { + // Rediriger vers la page de connexion si le rafraîchissement a échoué + throw new Error("RefreshAccessTokenError"); + } + // On injecte l'access token et le rôle dans la session accessible côté client session.accessToken = token.accessToken as string; + session.refreshToken = token.refreshToken as string; + session.error = token.error as string; session.user.first_name = token.first_name as string; session.user.last_name = token.last_name as string; session.user.username = token.username as string; @@ -48,12 +94,11 @@ export const authOptions: NextAuthOptions = { }, session: { strategy: "jwt", + maxAge: 60 * 60, // 1 heure }, events: { async signOut({ token }) { try { - console.log("Déconnexion Keycloak"); - console.log("Token", token); const issuerUrl = process.env.KEYCLOAK_ISSUER!; const logoutUrl = `${issuerUrl}/protocol/openid-connect/logout`; await fetch(logoutUrl, { diff --git a/front/types/next-auth.d.ts b/front/types/next-auth.d.ts index 66950b2..baaa168 100644 --- a/front/types/next-auth.d.ts +++ b/front/types/next-auth.d.ts @@ -10,6 +10,8 @@ declare module "next-auth" { role: string[]; } & DefaultSession["user"]; accessToken?: string; + refreshToken?: string; + error?: string; } interface JWT { @@ -18,6 +20,7 @@ declare module "next-auth" { last_name?: string; username?: string; role?: string[] | string | null; + error?: string; } interface User extends DefaultUser {