ai-chatbot/lib/access-keys/openapi-auth.ts

68 lines
1.6 KiB
TypeScript

import "server-only";
import { parseBearerAccessKey } from "@/lib/access-keys/token";
import {
authenticateUserAccessKey,
type AccessKeyPrincipal,
type AccessKeyScope,
} from "@/lib/access-keys/service";
export class OpenApiAuthError extends Error {
status: number;
code: string;
constructor(message: string, status: number, code: string) {
super(message);
this.name = "OpenApiAuthError";
this.status = status;
this.code = code;
}
}
export async function requireOpenApiPrincipal(
request: Request,
requiredScope?: AccessKeyScope
): Promise<AccessKeyPrincipal> {
const token = parseBearerAccessKey(request.headers.get("authorization"));
if (!token) {
throw new OpenApiAuthError(
"Missing or invalid Bearer Access Key",
401,
"ACCESS_KEY_REQUIRED"
);
}
const principal = await authenticateUserAccessKey(token);
if (!principal) {
throw new OpenApiAuthError(
"Access Key is invalid, expired, or revoked",
401,
"ACCESS_KEY_INVALID"
);
}
if (requiredScope && !principal.scopes.includes(requiredScope)) {
throw new OpenApiAuthError(
`Access Key is missing scope: ${requiredScope}`,
403,
"ACCESS_KEY_SCOPE_REQUIRED"
);
}
return principal;
}
export function openApiErrorResponse(error: unknown) {
if (error instanceof OpenApiAuthError) {
return Response.json(
{ success: false, error: error.message, code: error.code },
{ status: error.status }
);
}
return Response.json(
{ success: false, error: "OpenAPI request failed", code: "OPENAPI_ERROR" },
{ status: 500 }
);
}