Files
sistema_base/apps/web/lib/auth.config.ts

179 lines
6.2 KiB
TypeScript

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<Record<'username' | 'password', unknown>>,
): Promise<User | null> {
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;