From bc7231efa3187424225bbc34a0d76b0bf04ca751 Mon Sep 17 00:00:00 2001 From: alma Date: Wed, 16 Apr 2025 23:02:12 +0200 Subject: [PATCH] Neah version calendar fix 3 debuger ???? ?????????? --- components/providers.tsx | 3 +- lib/auth.ts | 89 +++++++++++++++++++++++++++++++++++----- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/components/providers.tsx b/components/providers.tsx index 987b2ac..704bda3 100644 --- a/components/providers.tsx +++ b/components/providers.tsx @@ -1,6 +1,7 @@ "use client"; import { SessionProvider } from "next-auth/react"; +import { authOptions } from "@/lib/auth"; interface ProvidersProps { children: React.ReactNode; @@ -8,7 +9,7 @@ interface ProvidersProps { export function Providers({ children }: ProvidersProps) { return ( - + {children} ); diff --git a/lib/auth.ts b/lib/auth.ts index fd846c9..a46fe0f 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -5,17 +5,38 @@ declare module 'next-auth' { interface User { id: string; email: string; - name?: string; + name?: string | null; role: string[]; } interface Session { - user: User; + user: { + id: string; + email: string; + name?: string | null; + role: string[]; + }; } interface Profile { + sub: string; + email?: string; + name?: string; roles?: string[]; } } +declare module 'next-auth/jwt' { + interface JWT { + id: string; + email: string; + name?: string; + role: string[]; + accessToken?: string; + refreshToken?: string; + accessTokenExpires?: number; + error?: string; + } +} + export const authOptions: NextAuthOptions = { providers: [ KeycloakProvider({ @@ -26,28 +47,76 @@ export const authOptions: NextAuthOptions = { ], session: { strategy: 'jwt', + maxAge: 30 * 24 * 60 * 60, // 30 days }, pages: { - signIn: '/login', + signIn: '/signin', + error: '/signin', }, callbacks: { async jwt({ token, account, profile }) { if (account && profile) { - // Store the Keycloak user ID token.id = profile.sub; token.email = profile.email || ''; token.name = profile.name; token.role = profile.roles || ['user']; + token.accessToken = account.access_token; + token.refreshToken = account.refresh_token; + token.accessTokenExpires = account.expires_at! * 1000; + } + + // Return previous token if not expired + if (Date.now() < (token.accessTokenExpires as number)) { + return token; + } + + // Token expired, try to refresh + try { + const response = await fetch( + `${process.env.KEYCLOAK_BASE_URL}/realms/${process.env.KEYCLOAK_REALM}/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 tokens = await response.json(); + + if (!response.ok) { + throw new Error('RefreshAccessTokenError'); + } + + return { + ...token, + accessToken: tokens.access_token, + refreshToken: tokens.refresh_token ?? token.refreshToken, + accessTokenExpires: Date.now() + tokens.expires_in * 1000, + }; + } catch (error) { + return { + ...token, + error: 'RefreshAccessTokenError', + }; } - return token; }, async session({ session, token }) { - if (token) { - session.user.id = token.id as string; - session.user.email = token.email as string; - session.user.name = token.name as string; - session.user.role = token.role as string[]; + if (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; + return session; }, },