@o3co/auth.policy-verifier.server
v0.3.1
Published
Express HTTP server for auth.policy-verifier. Provides `createApp` to assemble the application from modules and config, and `POST /verify` for authorization decisions.
Downloads
411
Readme
@o3co/auth.policy-verifier.server
Express HTTP server for auth.policy-verifier. Provides createApp to assemble the application from modules and config, and POST /verify for authorization decisions.
Install
npm install @o3co/auth.policy-verifier.serverPublic API
createApp
interface CreateAppOptions {
pathResolver: PathResolver;
config: AppConfig;
modules: Module[];
}
function createApp(options: CreateAppOptions): Promise<express.Express>Assembles and returns a configured Express application. Does not start listening — call app.listen(...) separately.
Steps performed:
- Creates
Registryinstances for attribute collector, rule collector, and resource parser factories. - Calls
mod.init(context)for each module in order, allowing each to register factory functions. - Instantiates attribute collectors and rule collectors from
config.attribute.collectorsandconfig.rule.collectorsby looking up the registered factory for eachcollectorname. - Instantiates the resource parser from
config.resource.parser. - Mounts
GET /healthcheckandPOST /verifyunderconfig.http.pathPrefix. - Returns the configured
express.Expressinstance.
pathResolver must be import.meta.resolve (or a compatible resolver) from the composition root. It is passed to modules that need to resolve module-relative paths.
createVerifyRouter
interface VerifyRouterConfig {
jwt: { secret: string; validate: boolean };
resourceParser: ResourceParser;
attributePipeline: AttributePipeline;
rulePipeline: RulePipeline;
}
function createVerifyRouter(config: VerifyRouterConfig): express.RouterReturns an Express Router that handles POST /verify. createApp calls this internally; use it directly only if you need to mount the router independently.
Request flow:
- Extract
Authorization: <type> <token>header. Returns 401 if missing. - If
validateistrue: verify the JWT with HS256 usingsecret. Returns 401 on failure. - If
validateisfalse: decode the JWT without verification. Returns 401 if the token is malformed. - Parse
req.body.resourcewithresourceParser; readreq.body.actionandreq.body.context. - Include
x-request-idheader inCollectorContext.headersif present (collectors can forward it to upstream calls they make). - Run
attributePipeline.collectandrulePipeline.collectin parallel; callevaluate. - Return
200 { decision: "allow" }or403 { decision: "deny", code, message }. - Return
500 { decision: "deny", code: "internal_error" }on unexpected errors.
AppConfigSchema / AppConfig
const AppConfigSchema = z.object({
http: z.object({
hostname: z.string().default("0.0.0.0"),
port: z.coerce.number().default(3000),
pathPrefix: z.string().default(""),
}),
oauth: z.object({
jwt: z.object({
secret: z.string(),
validate: z.boolean().default(true),
}),
}),
attribute: z.object({
collectors: z.array(z.object({ collector: z.string() }).passthrough()),
}),
rule: z.object({
collectors: z.array(z.object({ collector: z.string() }).passthrough()),
}),
resource: z.object({
parser: z.string().default("DotNotationResourceParser"),
}),
});
type AppConfig = z.infer<typeof AppConfigSchema>;Each entry in attribute.collectors and rule.collectors requires a collector field (the registered factory name). Additional fields are passed through to the factory as configuration.
POST /verify
Request
POST /verify HTTP/1.1
Authorization: Bearer <jwt>
Content-Type: application/json
x-request-id: <optional>
{
"resource": "project:1",
"action": "read",
"context": {}
}Response — allow
HTTP/1.1 200 OK
{ "decision": "allow" }Response — deny
HTTP/1.1 403 Forbidden
{ "decision": "deny", "code": "<code>", "message": "<message>" }Response — unexpected error
HTTP/1.1 500 Internal Server Error
{ "decision": "deny", "code": "internal_error" }Usage Example
import { resolve } from 'node:path'
import { parseFile } from '@o3co/ts.hocon'
import { validate } from '@o3co/ts.hocon/zod'
import { createApp, AppConfigSchema } from '@o3co/auth.policy-verifier.server'
import { builtinCollectorsModule } from '@o3co/auth.policy-verifier.builtins'
const config = validate(
parseFile(resolve(import.meta.dirname, '../config/application.conf')),
AppConfigSchema,
)
const app = await createApp({
pathResolver: import.meta.resolve,
config,
modules: [builtinCollectorsModule],
})
app.listen(config.http.port, config.http.hostname, () => {
console.log(`listening on ${config.http.hostname}:${config.http.port}`)
})To add a custom module, implement Module from @o3co/auth.policy-verifier.core and pass it in the modules array:
import type { Module } from '@o3co/auth.policy-verifier.core'
const customModule: Module = {
name: 'custom',
async init(context) {
context.attributeCollectorRegistry.register(
'MyRoleCollector',
(config) => new MyRoleCollector(config),
)
},
}
const app = await createApp({
pathResolver: import.meta.resolve,
config,
modules: [builtinCollectorsModule, customModule],
})See Also
@o3co/auth.policy-verifier.core— Types,evaluate,AttributePipeline,RulePipeline, Module infrastructure@o3co/auth.policy-verifier.builtins— Built-in collectors and parsers- auth.policy-verifier root README — Architecture overview, configuration reference, Docker
