create-apppaaaul
Version:
Create projects as paaauldev would
198 lines (163 loc) • 4.88 kB
text/typescript
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;
}