@beignet/core
Version:
Core framework primitives for Beignet
155 lines • 4.42 kB
JavaScript
/**
* Error thrown by auth helpers when a route or workflow requires a user but no
* authenticated user is available.
*/
export class AuthUnauthorizedError extends Error {
code = "UNAUTHORIZED";
/** HTTP status used when the server maps this framework error. */
status = 401;
constructor(message = "Unauthorized") {
super(message);
this.name = "AuthUnauthorizedError";
}
}
/**
* Error thrown by tenant helpers when a workflow requires a tenant scope but
* the current context has none.
*
* The server maps this to a framework-owned 403 response, mirroring how
* `AuthUnauthorizedError` maps to a framework-owned 401.
*/
export class TenantRequiredError extends Error {
code = "TENANT_REQUIRED";
status = 403;
constructor(message = "A tenant is required for this action.") {
super(message);
this.name = "TenantRequiredError";
}
}
function throwRequired(options, fallback) {
throw options?.error ? options.error() : fallback();
}
/**
* Return the authenticated session from `ctx.auth` or throw.
*
* Throws `AuthUnauthorizedError` (a framework-owned 401) by default. Pass
* `options.error` to throw an app-owned error instead.
*
* @example
* ```ts
* const session = requireSession(ctx);
* ```
*/
export function requireSession(ctx, options) {
if (!ctx.auth) {
throwRequired(options, () => new AuthUnauthorizedError());
}
return ctx.auth;
}
/**
* Return the authenticated user from `ctx.auth` or throw.
*
* The user type is inferred from the app's `ctx.auth` session. Throws
* `AuthUnauthorizedError` (a framework-owned 401) by default.
*
* @example
* ```ts
* const user = requireUser(ctx);
* ```
*/
export function requireUser(ctx, options) {
return requireSession(ctx, options).user;
}
/**
* Return the authenticated user's ID from `ctx.auth` or throw.
*
* Throws `AuthUnauthorizedError` (a framework-owned 401) by default.
*
* @example
* ```ts
* const userId = requireUserId(ctx);
* ```
*/
export function requireUserId(ctx, options) {
return requireUser(ctx, options).id;
}
/**
* Return the tenant scope from `ctx.tenant` or throw.
*
* Throws `TenantRequiredError` (a framework-owned 403) by default. Pass
* `options.error` to throw an app-owned error instead.
*
* @example
* ```ts
* const tenant = requireTenant(ctx);
* ```
*/
export function requireTenant(ctx, options) {
if (!ctx.tenant) {
throwRequired(options, () => new TenantRequiredError());
}
return ctx.tenant;
}
/**
* Return the tenant ID from `ctx.tenant` or throw.
*
* Throws `TenantRequiredError` (a framework-owned 403) by default.
*
* @example
* ```ts
* const tenantId = requireTenantId(ctx);
* ```
*/
export function requireTenantId(ctx, options) {
return requireTenant(ctx, options).id;
}
/**
* Create an auth port from a fixed session or request-aware session factory.
*
* This is useful for tests, examples, and simple apps. Production apps usually
* use a provider-backed auth port that verifies cookies, tokens, or sessions.
*
* @example
* ```ts
* const auth = createStaticAuth({
* user: { id: "user_1", name: "Ada" },
* });
* ```
*
* @param session - Fixed session, `null`, or a function that resolves a session
* from the request.
* @returns An `AuthPort` implementation backed by the provided session source.
*/
export function createStaticAuth(session) {
async function resolveSession(req) {
return typeof session === "function" ? session(req) : session;
}
return {
async getSession(req) {
return resolveSession(req);
},
async getUser(req) {
return (await resolveSession(req))?.user ?? null;
},
async requireUser(req) {
const user = (await resolveSession(req))?.user ?? null;
if (!user) {
throw new AuthUnauthorizedError();
}
return user;
},
};
}
/**
* Create an auth port that always treats requests as unauthenticated.
*
* Use this in tests or examples where auth is intentionally absent. It is not a
* security boundary; it simply returns `null` from `getSession`/`getUser` and
* throws from `requireUser`.
*
* @returns An `AuthPort` with no active session.
*/
export function createAnonymousAuth() {
return createStaticAuth(null);
}
//# sourceMappingURL=auth.js.map