refactor: rombak dari monorepo ke project tunggal — flat src/{domain,application,infrastructure,interfaces}, satu package.json tanpa workspaces, tsconfig paths → src/*, update install.sh/Dockerfile/CI
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* JWT authentication middleware for the API.
|
||||
* Mirrors `apps/interfaces/api/src/middleware/auth.rs`.
|
||||
*
|
||||
* Validates the `Authorization: Bearer <token>` header and injects the
|
||||
* validated subject into `req.user`. Refresh tokens are rejected on
|
||||
* protected routes — only access tokens are accepted.
|
||||
*/
|
||||
import { verifyToken } from "@zesdex/infrastructure";
|
||||
import type { ApiState } from "../state.ts";
|
||||
|
||||
/** Claims decoded from a valid JWT, attached to the request. */
|
||||
export interface JwtClaims {
|
||||
sub: string;
|
||||
}
|
||||
|
||||
/** Result of authentication: either the subject or an error status/detail. */
|
||||
export type AuthResult = { ok: true; sub: string } | { ok: false; status: number; detail: string };
|
||||
|
||||
/**
|
||||
* Authenticate a request against the API state's JWT secret.
|
||||
* Reads the `Authorization: Bearer <token>` header, verifies the signature
|
||||
* and token type (must be `access`), and returns the subject on success.
|
||||
*/
|
||||
export function authenticateRequest(state: ApiState, headers: Headers): AuthResult {
|
||||
const authHeader = headers.get("Authorization");
|
||||
if (!authHeader) {
|
||||
return { ok: false, status: 401, detail: "Missing or invalid Authorization header" };
|
||||
}
|
||||
const prefix = "Bearer ";
|
||||
if (!authHeader.startsWith(prefix)) {
|
||||
return { ok: false, status: 401, detail: "Missing or invalid Authorization header" };
|
||||
}
|
||||
const token = authHeader.slice(prefix.length).trim();
|
||||
if (!token) {
|
||||
return { ok: false, status: 401, detail: "Missing or invalid Authorization header" };
|
||||
}
|
||||
try {
|
||||
const claims = verifyToken(state.jwt_secret, token);
|
||||
if (claims.typ !== "access") {
|
||||
return {
|
||||
ok: false,
|
||||
status: 401,
|
||||
detail: "refresh tokens are not accepted on protected routes",
|
||||
};
|
||||
}
|
||||
return { ok: true, sub: claims.sub };
|
||||
} catch (e) {
|
||||
return { ok: false, status: 401, detail: `Invalid token: ${(e as Error).message}` };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user