UNPKG

create-apppaaaul

Version:

Create projects as paaauldev would

198 lines (163 loc) 4.88 kB
import { headers } from "next/headers"; import { auth } from "./auth"; import { rateLimiters } from "./rate-limit"; export interface AuthUser { id: string; email: string; role: string; banned: boolean; } export interface AuthResult { success: boolean; user?: AuthUser; error?: string; } export interface AuthorizationOptions { requireAuth?: boolean; allowedRoles?: string[]; allowSelf?: boolean; // Para permitir acceso a recursos propios resourceOwnerId?: string; // ID del propietario del recurso } /** * Verifica la autenticación del usuario actual */ export async function verifyAuth(): Promise<AuthResult> { try { const session = await auth.api.getSession({ headers: await headers(), }); if (!session?.user) { return { success: false, error: "Authentication required", }; } // Verificar si el usuario está baneado if (session.user.banned) { return { success: false, error: "Account banned", }; } return { success: true, user: { id: session.user.id, email: session.user.email, role: session.user.role ?? "user", banned: session.user.banned ?? false, }, }; } catch (error) { console.error("Authentication error:", error); return { success: false, error: "Authentication failed", }; } } /** * Verifica autorización basada en opciones */ export async function verifyAuthorization(options: AuthorizationOptions = {}): Promise<AuthResult> { const { requireAuth = true, allowedRoles = [], allowSelf = false, resourceOwnerId } = options; if (!requireAuth) { return { success: true }; } const authResult = await verifyAuth(); if (!authResult.success || !authResult.user) { return authResult; } const user = authResult.user; // Verificar roles permitidos if (allowedRoles.length > 0 && !allowedRoles.includes(user.role)) { // Si se permite acceso propio, verificar si es el propietario del recurso if (allowSelf && resourceOwnerId && user.id === resourceOwnerId) { return { success: true, user }; } return { success: false, error: "Insufficient permissions", }; } return { success: true, user }; } /** * Aplica rate limiting basado en el usuario */ export function checkRateLimit( limitType: keyof typeof rateLimiters, userId?: string, ): { success: boolean; error?: string } { const identifier = userId ?? "anonymous"; const result = rateLimiters[limitType].check(identifier); if (!result.success) { return { success: false, error: "Rate limit exceeded. Please try again later.", }; } return { success: true }; } /** * Middleware de seguridad combinado para server actions */ export function withSecurityMiddleware<T extends unknown[], R>( action: (...args: T) => Promise<R>, options: AuthorizationOptions & { rateLimitType?: keyof typeof rateLimiters; } = {}, ): (...args: T) => Promise<R> { return async (...args: T): Promise<R> => { // Verificar autorización const authResult = await verifyAuthorization(options); if (!authResult.success) { throw new Error(authResult.error ?? "Authorization failed"); } // Aplicar rate limiting si está especificado if (options.rateLimitType && authResult.user) { const rateLimitResult = checkRateLimit(options.rateLimitType, authResult.user.id); if (!rateLimitResult.success) { throw new Error(rateLimitResult.error ?? "Rate limit exceeded"); } } // Ejecutar la acción return await action(...args); }; } /** * Verifica si el usuario es admin */ export async function requireAdmin(): Promise<AuthUser> { const result = await verifyAuthorization({ allowedRoles: ["admin"], }); if (!result.success || !result.user) { throw new Error(result.error ?? "Admin access required"); } return result.user; } /** * Verifica si el usuario está autenticado */ export async function requireAuth(): Promise<AuthUser> { const result = await verifyAuth(); if (!result.success || !result.user) { throw new Error(result.error ?? "Authentication required"); } return result.user; } /** * Verifica si el usuario puede acceder a un recurso (admin o propietario) */ export async function requireOwnershipOrAdmin(resourceOwnerId: string): Promise<AuthUser> { const result = await verifyAuthorization({ allowedRoles: ["admin"], allowSelf: true, resourceOwnerId, }); if (!result.success || !result.user) { throw new Error(result.error ?? "Access denied"); } return result.user; }