diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-12-06 17:05:21 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-12-06 17:37:04 +0900 |
| commit | 811458427593a4172a2cd535cc768db375350dca (patch) | |
| tree | 6c4f46c96b6f29392dc19d591e39e03c187033a1 /src/server/middleware/auth.ts | |
| parent | 9736a8981fbd6c6defbd67517ca23904fc844629 (diff) | |
| download | kioku-811458427593a4172a2cd535cc768db375350dca.tar.gz kioku-811458427593a4172a2cd535cc768db375350dca.tar.zst kioku-811458427593a4172a2cd535cc768db375350dca.zip | |
feat(dev): change architecture and directory structure
Diffstat (limited to 'src/server/middleware/auth.ts')
| -rw-r--r-- | src/server/middleware/auth.ts | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/src/server/middleware/auth.ts b/src/server/middleware/auth.ts new file mode 100644 index 0000000..51b4d9d --- /dev/null +++ b/src/server/middleware/auth.ts @@ -0,0 +1,65 @@ +import type { Context, Next } from "hono"; +import { verify } from "hono/jwt"; +import { Errors } from "./error-handler.js"; + +const JWT_SECRET = process.env.JWT_SECRET; +if (!JWT_SECRET) { + throw new Error("JWT_SECRET environment variable is required"); +} + +export interface AuthUser { + id: string; +} + +interface JWTPayload { + sub: string; + iat: number; + exp: number; +} + +/** + * Auth middleware that validates JWT tokens from Authorization header + * Sets the authenticated user in context variables + */ +export async function authMiddleware(c: Context, next: Next) { + const authHeader = c.req.header("Authorization"); + + if (!authHeader) { + throw Errors.unauthorized("Missing Authorization header", "MISSING_AUTH"); + } + + if (!authHeader.startsWith("Bearer ")) { + throw Errors.unauthorized( + "Invalid Authorization header format", + "INVALID_AUTH_FORMAT", + ); + } + + const token = authHeader.slice(7); + + try { + const payload = (await verify(token, JWT_SECRET)) as unknown as JWTPayload; + + const user: AuthUser = { + id: payload.sub, + }; + + c.set("user", user); + + await next(); + } catch { + throw Errors.unauthorized("Invalid or expired token", "INVALID_TOKEN"); + } +} + +/** + * Helper function to get the authenticated user from context + * Throws if user is not authenticated + */ +export function getAuthUser(c: Context): AuthUser { + const user = c.get("user") as AuthUser | undefined; + if (!user) { + throw Errors.unauthorized("Not authenticated", "NOT_AUTHENTICATED"); + } + return user; +} |
