import { MiddlewareHandler } from "hono"; import { getCookie } from "hono/cookie"; import { Effect, ManagedRuntime } from "effect"; import { AuthService } from "../services/AuthService.js"; import { PasswordService } from "../services/PasswordService.js"; import { TokenService } from "../services/TokenService.js"; import { UserRepository } from "../services/UserRepository.js"; import { SessionRepository } from "../services/SessionRepository.js"; import { AuditLogService } from "../services/AuditLogService.js"; import { DatabaseService } from "../../database/db.js"; import { Session, User } from "../../../shared/types.js"; // Extend Hono Context with user and session declare module "hono" { interface ContextVariableMap { user: User; session: Session; } } // Type for the application runtime context export type AppRuntimeContext = | PasswordService | TokenService | UserRepository | SessionRepository | AuditLogService | AuthService | DatabaseService; /** * Factory function to create auth middleware with proper Effect-TS ManagedRuntime injection * @param runtime - The Effect-TS ManagedRuntime with all required services * @returns Configured Hono middleware handler */ export const createAuthMiddleware = ( runtime: ManagedRuntime.ManagedRuntime ): MiddlewareHandler => { return async (c, next) => { // Extract token from Authorization header or cookie let token: string | undefined; const authHeader = c.req.header("Authorization"); if (authHeader?.startsWith("Bearer ")) { token = authHeader.substring(7); } else { token = getCookie(c, "auth_token"); } if (!token) { return c.json( { error: "UNAUTHORIZED", message: "No authentication token provided", }, 401 ); } // Verify token using AuthService with runtime const verifyEffect = Effect.gen(function* () { const authService = yield* AuthService; return yield* authService.verifyToken(token); }); const result = await runtime.runPromiseExit(verifyEffect); if (result._tag === "Failure") { const cause = result.cause; if (cause._tag === "Fail") { const failureError = cause.error; if (failureError && typeof failureError === "object" && "_tag" in failureError) { const typedError = failureError as { _tag: string; message: string; }; switch (typedError._tag) { case "AuthError": return c.json( { error: "AUTH_ERROR", message: typedError.message, }, 401 ); case "DatabaseError": return c.json( { error: "INTERNAL_ERROR", message: "Database error occurred", }, 500 ); } } } // Handle unknown errors return c.json( { error: "INTERNAL_ERROR", message: "An unexpected error occurred", }, 500 ); } // Success - inject user and session into context const verifyResult = result.value; c.set("user", verifyResult.user); c.set("session", verifyResult.session); return await next(); }; };