117 lines
3.2 KiB
TypeScript
117 lines
3.2 KiB
TypeScript
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
import { supabase, findOneGpi } from '../config/supabase.js';
|
|
import { IAppUser } from './authMiddleware.js';
|
|
|
|
const LOGTO_URL = process.env.LOGTO_URL || 'https://logto-admin-bzlued1boxl3t8ewsyn99an9.187.77.227.172.sslip.io';
|
|
const APP_ID = process.env.LOGTO_APP_ID || 'gpi-app-final';
|
|
const jwks = createRemoteJWKSet(new URL(`${LOGTO_URL}/oidc/jwks`));
|
|
|
|
export type AppUser = IAppUser;
|
|
|
|
export async function authenticateRequest(req: any): Promise<IAppUser | null> {
|
|
const authHeader = req.headers.authorization;
|
|
|
|
if (!authHeader?.startsWith('Bearer ')) {
|
|
return null;
|
|
}
|
|
|
|
const token = authHeader.substring(7);
|
|
|
|
try {
|
|
const { payload } = await jwtVerify(token, jwks, {
|
|
issuer: `${LOGTO_URL}/oidc`,
|
|
audience: APP_ID
|
|
});
|
|
|
|
const logtoId = payload.sub as string;
|
|
|
|
// Primeiro tenta pelo Logto ID
|
|
let user = await findOneGpi('users', { logto_id: logtoId });
|
|
|
|
// Se não encontrar, tenta pelo email (se houver no payload do token)
|
|
if (!user && payload.email) {
|
|
const email = payload.email as string;
|
|
user = await findOneGpi('users', { email });
|
|
|
|
if (user) {
|
|
// Vincula o Logto ID ao usuário existente
|
|
await supabase
|
|
.from('users')
|
|
.update({ logto_id: logtoId })
|
|
.eq('id', user.id);
|
|
|
|
user.logto_id = logtoId;
|
|
console.log(`[Auth] Usuário ${email} vinculado ao Logto ID ${logtoId}`);
|
|
}
|
|
}
|
|
|
|
// Auto-registro se não encontrar
|
|
if (!user) {
|
|
console.log(`[Auth] Usuário Logto ${logtoId} sem registro no GPI. Criando...`);
|
|
|
|
const email = (payload.email as string) || '';
|
|
const name = (payload.name as string) || (payload.username as string) || email.split('@')[0];
|
|
|
|
const { data: newUser, error: createError } = await supabase
|
|
.from('users')
|
|
.insert({
|
|
email,
|
|
name,
|
|
logto_id: logtoId,
|
|
role: 'user'
|
|
})
|
|
.select()
|
|
.single();
|
|
|
|
if (createError) {
|
|
console.error('[Auth] Erro ao auto-registrar usuário:', createError);
|
|
return null;
|
|
}
|
|
|
|
user = newUser;
|
|
console.log(`[Auth] Novo usuário auto-registrado: ${email}`);
|
|
}
|
|
|
|
return {
|
|
id: user.id || user._id,
|
|
logtoId: user.logto_id,
|
|
email: user.email,
|
|
name: user.name,
|
|
role: user.role
|
|
};
|
|
} catch (error) {
|
|
console.error('[Auth] Erro ao verificar token:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function requireAuth() {
|
|
return async (req: any, res: any, next: any) => {
|
|
const user = await authenticateRequest(req);
|
|
|
|
if (!user) {
|
|
return res.status(401).json({ error: 'Unauthorized' });
|
|
}
|
|
|
|
req.appUser = user;
|
|
next();
|
|
};
|
|
}
|
|
|
|
export function requireRole(roles: string[]) {
|
|
return async (req: any, res: any, next: any) => {
|
|
const user = await authenticateRequest(req);
|
|
|
|
if (!user) {
|
|
return res.status(401).json({ error: 'Unauthorized' });
|
|
}
|
|
|
|
if (!roles.includes(user.role)) {
|
|
return res.status(403).json({ error: 'Forbidden' });
|
|
}
|
|
|
|
req.appUser = user;
|
|
next();
|
|
};
|
|
}
|