Files
online-energieausweis/src/lib/middleware/authorization.ts
2025-03-25 14:01:13 -03:00

120 lines
2.7 KiB
TypeScript

import { decodeToken } from "#lib/auth/token.js";
import { hashPassword } from "#lib/password.js";
import { prisma } from "#lib/server/prisma.js";
import { APIError, TypesafeAPIContextWithRequest } from "astro-typesafe-api/server";
import { z } from "zod";
export async function checkAuthorizationHeaderNoThrow(authorization: string): Promise<ReturnType<typeof checkAuthorizationHeader> | null> {
try {
return await checkAuthorizationHeader(authorization)
} catch(e) {
return null
}
}
export async function checkAuthorizationHeader(authorization: string) {
if (!authorization) {
throw new APIError({
code: "BAD_REQUEST",
message: "Request is missing an 'Authorization' header."
})
}
if (authorization.startsWith("Basic")) {
const payload = btoa(authorization.split(" ")[1]).split(":");
if (payload.length !== 2) {
throw new APIError({
code: "BAD_REQUEST",
message: "Malformed 'Authorization' header."
})
}
const [email, password] = payload;
const user = await prisma.benutzer.findUnique({
where: {
email
}
})
if (!user || user.passwort !== hashPassword(password)) {
throw new APIError({
code: "UNAUTHORIZED",
message: "Unknown combination of email and password."
})
}
return user;
} else if (authorization.startsWith("Bearer")) {
const token = authorization.split(" ")[1]
if (!token) {
throw new APIError({
code: "BAD_REQUEST",
message: "Malformed 'Authorization' header."
})
}
const payload = decodeToken(token)
if ((payload.exp || 0) < Date.now()) {
throw new APIError({
code: "UNAUTHORIZED",
message: "Access Token has expired."
})
}
const user = await prisma.benutzer.findUnique({
where: {
id: payload.id
}
})
if (!user) {
throw new APIError({
code: "UNAUTHORIZED",
message: "Invalid Bearer Token."
})
}
return user;
}
throw new APIError({
code: "BAD_REQUEST",
message: "Invalid authorization method in 'Authorization' header."
})
}
export async function authorizationMiddleware(input: any, ctx: TypesafeAPIContextWithRequest<any>) {
return await checkAuthorizationHeader(ctx.request.headers.get("Authorization"));
}
export async function maybeAuthorizationMiddleware(input: any, ctx: TypesafeAPIContextWithRequest<any>) {
try {
return authorizationMiddleware(input, ctx)
} catch(e) {
return null;
}
}
export const authorizationHeaders = {
Authorization: z.string()
}
export async function adminMiddleware(input: any, ctx: TypesafeAPIContextWithRequest<any>) {
try {
const user = await authorizationMiddleware(input, ctx)
if (user.rolle === "ADMIN") {
return user
}
} catch(e) {
}
throw new APIError({
code: "FORBIDDEN",
"message": "Diese Route ist für Admins vorbehalten."
})
}