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

150 lines
4.9 KiB
TypeScript

// lib/auth.config.ts
import { SignInAction } from '@/feactures/auth/actions/login-action';
import { logoutAction } from '@/feactures/auth/actions/logout-action';
import { CredentialsSignin, NextAuthConfig, Session, User } from 'next-auth';
import { DefaultJWT, JWT } 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,
};
},
}),
],
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,
};
}
return token;
},
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.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;
},
},
events: {
async signOut(message) {
// 1. verificamos que venga token (puede no venir con algunos providers)
const token = (message as { token?: JWT }).token;
if (!token?.access_token) return;
try {
await logoutAction(String(token?.id));
} catch {
/* silencioso para que next-auth siempre cierre */
}
},
},
} satisfies NextAuthConfig;
export default authConfig;