import { SignInAction } from '@/feactures/auth/actions/login-action'; import { resfreshTokenAction } from '@/feactures/auth/actions/refresh-token-action'; import { CredentialsSignin, NextAuthConfig, Session, User } from 'next-auth'; import { DefaultJWT } from 'next-auth/jwt'; import CredentialProvider from 'next-auth/providers/credentials'; // Define los tipos para tus respuestas de SignInAction interface SignInSuccessResponse { message: string; user: { email: string; username: string; id: number; rol: Array<{ id: number; rol: string }>; fullname: string; }; tokens: { access_token: string; access_expire_in: number; refresh_token: string; refresh_expire_in: number; }; } // **CAMBIO AQUÍ**: `type: string;` en lugar de una unión de literales interface SignInErrorResponse { type: string; // Si SignInAction puede devolver cualquier string aquí message: string; details?: any; } // Unión de tipos para el resultado de SignInAction, AHORA INCLUYE `null` type SignInActionResult = SignInSuccessResponse | SignInErrorResponse | null; const authConfig: NextAuthConfig = { providers: [ CredentialProvider({ credentials: { username: { type: 'username', }, password: { type: 'password', }, }, async authorize( credentials: Partial>, ): Promise { const credential = { username: credentials?.username as string, password: credentials?.password as string, }; // Asigna el tipo `SignInActionResult` que ahora incluye `null` const response: SignInActionResult = await SignInAction(credential); // **NUEVO: Manejar el caso `null` primero** if (response === null) { console.error("SignInAction returned null, indicating a potential issue before API call or generic error."); throw new CredentialsSignin("Error de inicio de sesión inesperado."); } // Tipo Guarda: Verificar la respuesta de error if ( 'type' in response && (response.type === 'API_ERROR' || response.type === 'VALIDATION_ERROR' || response.type === 'UNKNOWN_ERROR') // Incluye todos los tipos de error posibles ) { // Si es un error, lánzalo. Este camino termina aquí. throw new CredentialsSignin("Error en la API:" + response.message); } if (!('user' in response)) { // Esto solo ocurriría si SignInAction devolvió un objeto que no es null, // no es un error conocido por 'type', PERO tampoco tiene la propiedad 'user'. // Es un caso de respuesta inesperada del API. console.error("Respuesta de SignInAction con formato inesperado: falta la propiedad 'user'."); throw new CredentialsSignin("Error en el formato de la respuesta del servidor."); } return { id: response?.user.id?.toString() ?? '0', username: response?.user.username ?? '', fullname: response?.user.fullname ?? '', email: response?.user.email ?? '', role: response?.user.rol ?? [], // Add role array access_token: response?.tokens.access_token ?? '', access_expire_in: response?.tokens.access_expire_in ?? 0, refresh_token: response?.tokens.refresh_token ?? '', refresh_expire_in: response?.tokens.refresh_expire_in ?? 0, }; }, }), ], pages: { signIn: '/', //sigin page }, callbacks: { async jwt({ token, user }:{ user: User token: any }) { // 1. Manejar el inicio de sesión inicial // El `user` solo se proporciona en el primer inicio de sesión. if (user) { return { id: user.id, username: user.username, fullname: user.fullname, email: user.email, role: user.role, access_token: user.access_token, access_expire_in: user.access_expire_in, refresh_token: user.refresh_token, refresh_expire_in: user.refresh_expire_in } // return token; } // 2. Si no es un nuevo login, verificar la expiración del token const now = Math.floor(Date.now() / 1000); // Usar Math.floor para un número entero // Verificar si el token de acceso aún es válido if (now < (token.access_expire_in as number)) { return token; // Si no ha expirado, no hacer nada y devolver el token actual } // console.log("Now Access Expire:",token.access_expire_in); // 3. Si el token de acceso ha expirado, verificar el refresh token // console.log("Access token ha expirado. Verificando refresh token..."); if (now > (token.refresh_expire_in as number)) { // console.log("Refresh token ha expirado. Forzando logout."); return null; // Forzar el logout al devolver null } // console.log("token:", token.refresh_token); // 4. Si el token de acceso ha expirado pero el refresh token es válido, renovar // console.log("Renovando token de acceso..."); try { const res = await resfreshTokenAction({ token: token.refresh_token as string }); if (!res || !res.tokens) { throw new Error('Fallo en la respuesta de la API de refresco.'); } // console.log("Old Access Expire:", token.access_expire_in); // console.log("New Access Expire:", res.tokens.access_expire_in); // console.log("token:", token.refresh_token); // Actualizar el token directamente con los nuevos valores token.access_token = res.tokens.access_token; token.access_expire_in = res.tokens.access_expire_in; token.refresh_token = res.tokens.refresh_token; token.refresh_expire_in = res.tokens.refresh_expire_in; return token; } catch (error) { console.error("Error al renovar el token: ", error); return null; // Fallo al renovar, forzar logout } }, async session({ session, token }: { session: Session; token: any }) { session.access_token = token.access_token as string; session.access_expire_in = token.access_expire_in as number; session.refresh_token = token.refresh_token as string; session.refresh_expire_in = token.refresh_expire_in as number; session.user = { id: token.id as number, username: token.username as string, fullname: token.fullname as string, email: token.email as string, role: token.role as Array<{ id: number; rol: string }>, }; return session; }, }, } satisfies NextAuthConfig; export default authConfig;