feat: add authorization bootstrap and HTTP enforcement
Wires Core's identity and authorization domains into apps/api over one shared, validated PostgreSQL pool; adds an explicit, single-use, concurrency-safe platform-owner bootstrap CLI; adds a smallest-possible immutable permission registry with one Core-owned restricted permission (core.authorization.grants.manage); and adds reusable Hono authorization enforcement plus a narrowly scoped /api/v1/grants HTTP API for managing explicit grants only, gated by an exact trusted Origin on every mutation.
This commit is contained in:
+27
-2
@@ -1,13 +1,27 @@
|
||||
import { isAuthenticatedPrincipal } from "@ekklesia/contracts";
|
||||
import {
|
||||
type AuthorizationEvaluator,
|
||||
type AuthorizationStore,
|
||||
CORE_PERMISSION_REGISTRY,
|
||||
} from "@ekklesia/core/authorization";
|
||||
import { type EkklesiaIdentityRuntime, IdentityRuntimeError } from "@ekklesia/core/identity";
|
||||
import { Hono } from "hono";
|
||||
import type { Context } from "hono";
|
||||
import { cors } from "hono/cors";
|
||||
import { createGrantsRoutes } from "./grants-routes.js";
|
||||
|
||||
export interface CreateApiAppAuthorizationOptions {
|
||||
readonly evaluator: AuthorizationEvaluator;
|
||||
readonly store: AuthorizationStore;
|
||||
}
|
||||
|
||||
export interface CreateApiAppOptions {
|
||||
readonly authorization: CreateApiAppAuthorizationOptions;
|
||||
readonly runtime: EkklesiaIdentityRuntime;
|
||||
/** Exact allowed browser origins. Never a wildcard; combined with `credentials: true`, only an
|
||||
* exact, reflected match is ever returned in `Access-Control-Allow-Origin`. */
|
||||
* exact, reflected match is ever returned in `Access-Control-Allow-Origin`. Also the trusted set
|
||||
* `requireTrustedOrigin` checks for grant-mutation routes — there is no second, competing origin
|
||||
* configuration. */
|
||||
readonly trustedOrigins: readonly string[];
|
||||
}
|
||||
|
||||
@@ -20,7 +34,7 @@ function sanitizedErrorResponse(c: Context, error: unknown, status: 500 | 503) {
|
||||
/**
|
||||
* Pure Hono application factory: no I/O, no environment reads, no process side effects. Suitable
|
||||
* for unit and integration tests via `app.fetch` directly, with a fully injected identity runtime
|
||||
* standing in for real Better Auth/PostgreSQL where appropriate.
|
||||
* and authorization evaluator/store standing in for real Better Auth/PostgreSQL where appropriate.
|
||||
*/
|
||||
export function createApiApp(options: CreateApiAppOptions): Hono {
|
||||
const { runtime } = options;
|
||||
@@ -62,6 +76,17 @@ export function createApiApp(options: CreateApiAppOptions): Hono {
|
||||
}
|
||||
});
|
||||
|
||||
app.route(
|
||||
"/",
|
||||
createGrantsRoutes({
|
||||
evaluator: options.authorization.evaluator,
|
||||
registry: CORE_PERMISSION_REGISTRY,
|
||||
runtime,
|
||||
store: options.authorization.store,
|
||||
trustedOrigins: allowedOrigins,
|
||||
}),
|
||||
);
|
||||
|
||||
app.notFound((c) => {
|
||||
c.header("Cache-Control", "no-store");
|
||||
return c.json({ code: "NOT_FOUND" }, 404);
|
||||
|
||||
Reference in New Issue
Block a user