aboutsummaryrefslogtreecommitdiffhomepage
path: root/pkgs/server/src/middleware/auth.ts
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-12-03 05:54:50 +0900
committernsfisis <nsfisis@gmail.com>2025-12-04 23:26:31 +0900
commit4d30f46d4691c9aead411b893b1ab279b05d439c (patch)
treeb5c05015a4b367daf7d9d885f67a7fb1cbc0d0bf /pkgs/server/src/middleware/auth.ts
parent0763153865e2157e0d06c946993dd8b235b06c83 (diff)
downloadkioku-4d30f46d4691c9aead411b893b1ab279b05d439c.tar.gz
kioku-4d30f46d4691c9aead411b893b1ab279b05d439c.tar.zst
kioku-4d30f46d4691c9aead411b893b1ab279b05d439c.zip
feat(auth): add auth middleware for JWT validation
Add middleware that validates JWT tokens from Authorization header and sets authenticated user in request context. Includes helper function getAuthUser() to retrieve user from context with proper error handling. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'pkgs/server/src/middleware/auth.ts')
-rw-r--r--pkgs/server/src/middleware/auth.ts65
1 files changed, 65 insertions, 0 deletions
diff --git a/pkgs/server/src/middleware/auth.ts b/pkgs/server/src/middleware/auth.ts
new file mode 100644
index 0000000..c295834
--- /dev/null
+++ b/pkgs/server/src/middleware/auth.ts
@@ -0,0 +1,65 @@
+import type { Context, Next } from "hono";
+import { verify } from "hono/jwt";
+import { Errors } from "./error-handler";
+
+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;
+}