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, }: { token: any; user: User; }) { try { // Si es un nuevo login, asignamos los datos if (user) { token.id = user.id; token.username = user.username; token.fullname = user.fullname; token.email = user.email; token.role = user.role; token.access_token = user.access_token; token.access_expire_in = user.access_expire_in; token.refresh_token = user.refresh_token; token.refresh_expire_in = user.refresh_expire_in; return token; // IMPORTANTE: retornamos el token aquí para evitar que entre en el siguiente 'if' } // Verificar si el access_token ha expirado const now = Date.now() / 1000; if (now < (token.access_expire_in as number)) { return token; // Si el token no ha expirado, lo retornamos sin cambios } // Si el access_token ha expirado, verificar el refresh_token if (now > (token.refresh_expire_in as number)) { console.log("Refresh token ha expirado. Forzando logout."); return null; // Forzar logout } // Si el access_token ha expirado pero el refresh_token es válido, renovar console.log("Renovando token de acceso..."); const res = await resfreshTokenAction({ token: token.refresh_token as string, }); console.log("Pepe"); if (!res) throw new Error('Fallo al renovar el token'); // Actualizar el token con la nueva información return { ...token, access_token: res.tokens.access_token, access_expire_in: res.tokens.access_expire_in, refresh_token: res.tokens.refresh_token, refresh_expire_in: res.tokens.refresh_expire_in, }; } catch (error) { console.error("Error al renovar el token: ", error); return null; // Fallo al renovar, forzar logout } }, async session({ session, token }: { session: Session; token: DefaultJWT }) { 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;