105 lines
2.8 KiB
TypeScript
105 lines
2.8 KiB
TypeScript
import { ErrorResponse, Unkey } from "@aigxion/api";
|
|
import type { Context, MiddlewareHandler } from "hono";
|
|
import { HTTPException } from "hono/http-exception";
|
|
import { version } from "../package.json";
|
|
|
|
export type UnkeyContext = {
|
|
valid: boolean;
|
|
ownerId?: string | undefined;
|
|
meta?: unknown;
|
|
expires?: number | undefined;
|
|
remaining?: number | undefined;
|
|
ratelimit?:
|
|
| {
|
|
limit: number;
|
|
remaining: number;
|
|
reset: number;
|
|
}
|
|
| undefined;
|
|
code?:
|
|
| "NOT_FOUND"
|
|
| "RATE_LIMITED"
|
|
| "FORBIDDEN"
|
|
| "USAGE_EXCEEDED"
|
|
| "UNAUTHORIZED"
|
|
| "DISABLED"
|
|
| "INSUFFICIENT_PERMISSIONS"
|
|
| undefined;
|
|
environment?: string;
|
|
};
|
|
|
|
export type UnkeyConfig = {
|
|
/**
|
|
* The apiId to verify against. Only keys belonging to this api will be valid.
|
|
*/
|
|
apiId: string;
|
|
|
|
/**
|
|
*
|
|
* By default telemetry data is enabled, and sends:
|
|
* runtime (Node.js / Edge)
|
|
* platform (Node.js / Vercel / AWS)
|
|
* SDK version
|
|
*/
|
|
disableTelemetry?: boolean;
|
|
|
|
/**
|
|
* How to get the key from the request
|
|
* Usually the key is provided in an `Authorization` header, but you can do what you want.
|
|
*
|
|
* Return the key as string, or undefined if it doesn't exist.
|
|
*
|
|
* You can also override the response given to the caller by returning a `Response`
|
|
*
|
|
* @default `c.req.header("Authorization")?.replace("Bearer ", "")`
|
|
*/
|
|
getKey?: (c: Context) => string | undefined | Response;
|
|
|
|
/**
|
|
* Automatically return a custom response when a key is invalid
|
|
*/
|
|
handleInvalidKey?: (c: Context, result: UnkeyContext) => Response | Promise<Response>;
|
|
|
|
/**
|
|
* What to do if things go wrong
|
|
*/
|
|
onError?: (c: Context, err: ErrorResponse["error"]) => Response | Promise<Response>;
|
|
};
|
|
|
|
export function unkey(config: UnkeyConfig): MiddlewareHandler {
|
|
return async (c, next) => {
|
|
const key = config.getKey
|
|
? config.getKey(c)
|
|
: c.req.header("Authorization")?.replace("Bearer ", "") ?? null;
|
|
if (!key) {
|
|
return c.json({ error: "unauthorized" }, { status: 401 });
|
|
}
|
|
if (typeof key !== "string") {
|
|
return key;
|
|
}
|
|
|
|
const unkeyInstance = new Unkey({
|
|
rootKey: "public",
|
|
disableTelemetry: config.disableTelemetry,
|
|
wrapperSdkVersion: `@unkey/hono@${version}`,
|
|
});
|
|
|
|
const res = await unkeyInstance.keys.verify({ key, apiId: config.apiId });
|
|
if (res.error) {
|
|
if (config.onError) {
|
|
return config.onError(c, res.error);
|
|
}
|
|
throw new HTTPException(500, {
|
|
message: `unkey error: [CODE: ${res.error.code}] - [REQUEST_ID: ${res.error.requestId}] - ${res.error.message} - read more at ${res.error.docs}`,
|
|
});
|
|
}
|
|
|
|
if (!res.result.valid && config.handleInvalidKey) {
|
|
return config.handleInvalidKey(c, res.result);
|
|
}
|
|
|
|
c.set("unkey", res.result);
|
|
await next();
|
|
};
|
|
}
|