Getting Started
AuthHub provides fine-grained authorization for healthcare applications using Relationship-Based Access Control (ReBAC). Integrate with our gRPC/Connect APIs to check permissions, manage relationships, and define authorization schemas.
Base URL
https://api.authhub.cloud
All API endpoints are served over gRPC and Connect (HTTP/JSON) protocols.
Authentication
All API requests require mutual TLS (mTLS) authentication using your tenant certificate. You can download your certificate from the Console under Certificates.
Authorization: Bearer <your-tenant-token>
X-Tenant-ID: <your-tenant-id>
X-Sub-Tenant-ID: <your-sub-tenant-id>
Content-Type: application/jsonQuick Start
- 1Register your organisation and provision a tenant via the Console.
- 2Download your mTLS certificate from the Certificates page.
- 3Write your authorization schema (or use the healthcare starter template).
- 4Write relationship tuples to represent your data model.
- 5Call CheckPermission at runtime to authorize access decisions.
API Reference
AuthHub exposes a comprehensive authorization and governance platform organized across core runtime protocols (ReBAC, AuthZEN, SCIM, Workload Identity) and specialized control-plane subsystems (Non-Human Identities, Business Governance Signals, Autonomous Governance & Safety, and Continuous Production Testing).
Specialist & Autonomous Control-Plane APIs100% Platform Coverage
Dedicated high-performance REST APIs for identity management, business signal integration, and safety substrate governance.
NHI Management API
/api/v1/tenant/nhis/*
Non-Human Identities lifecycle for service accounts, AI agents, CI/CD runners, dynamic JIT elevation, certification campaigns, and JML automation.
Governance Signal API
/api/v1/tenant/governance/*
Bridge business signals (HR JML, SIEM alerts, vulnerability scanners, cost centres) into real-time runtime authorization circuit breakers.
Autonomous Governance & Safety
/api/v1/tenant/governance/autonomous-policies/*
Β§7 Policy administration, dual-control human-in-the-loop approvals, blast-radius change classes, and autonomous feature workers with safety substrate.
Database Access Gateways
/v1/tenant/database-connections/*
Onboard cloud databases via REST API. Generates isolated SpiceDB query gateways with SARC AST checks, HMAC PII masking, and Clock-1 kill switches.
Interactive OpenAPI 3.1 Explorer & Machine-Readable Specs
Explore 137+ endpoints in Scalar UI or download raw OpenAPI 3.1 JSON / YAML schemas for code generation.
Protocol: gRPC + Connect (HTTP/JSON)
All gRPC endpoints are accessible via HTTP/JSON using the Connect protocol. The URL format is:
POST https://authhub.nhs.uk/{package.Service}/{Method}Example: POST /nhs.rebac.v1.PermissionCheckService/CheckPermission
Headers: Content-Type: application/json, Authorization: Bearer <token>
Group 1: ReBAC Core (SpiceDB / gRPC & REST)
Google Zanzibar-style relationship-based access control. Define schemas, write relationship tuples, and check permissions in real-time.
CheckPermission
Check whether a subject has a specific permission on a resource. This is your primary runtime authorization call.
π‘ Consistency: Use fullyConsistent: true for security-critical decisions (e.g., break-glass). Use minimizeLatency: true (default) for high-throughput reads where eventual consistency is acceptable (~1-2s lag).
/nhs.rebac.v1.PermissionCheckService/CheckPermission{
"sub_tenant_id": "your-sub-tenant-id",
"subject": {
"object": { "objectType": "user", "objectId": "dr-smith" }
},
"permission": "view_record",
"resource": {
"objectType": "patient",
"objectId": "patient-123"
},
"consistency": {
"fullyConsistent": true
}
}{
"decision": "DECISION_ALLOW",
"zeta_token": "GhUKEzE3MTk4NTcxODkyMzg1NTE4MjEYASAB",
"break_glass": false,
"enforcement_state_version": 42
}BatchCheckPermissions
Check up to 100 permissions atomically in a single round-trip over gRPC / Connect.
/nhs.rebac.v1.PermissionCheckService/BatchCheckPermissions{
"sub_tenant_id": "your-sub-tenant-id",
"checks": [
{
"subject_type": "user",
"subject_id": "dr-smith",
"permission": "view_record",
"object_type": "patient",
"object_id": "patient-123"
},
{
"subject_type": "user",
"subject_id": "dr-smith",
"permission": "prescribe_medication",
"object_type": "patient",
"object_id": "patient-123"
}
]
}{
"results": [
{
"decision": {
"decision": "DECISION_ALLOW",
"zeta_token": "GhUKEzE3MTk4NTcxODkyMzg1NTE4MjEYASAB",
"break_glass": false
}
},
{
"decision": {
"decision": "DECISION_DENY",
"zeta_token": "GhUKEzE3MTk4NTcxODkyMzg1NTE4MjEYASAB",
"break_glass": true
}
}
]
}Check Permission (REST API)
Direct REST alternative to gRPC CheckPermission. Automatically evaluates active governance suspensions in Redis (with break-glass emergency bypass) before resolving relationship graphs in SpiceDB.
π‘οΈ Governance & Break-Glass: If the subject is suspended by an active governance signal (e.g. GMC registry freeze or medical credential revocation), this endpoint returns allowed: false with reason: "GOVERNANCE_SUSPENSION", unless an authorized emergency break-glass override is actively held in Redis.
POST /api/v1/tenant/permissions/check{
"subjectType": "user",
"subjectId": "dr-smith",
"permission": "view_record",
"resourceType": "patient",
"resourceId": "patient-123"
}{
"allowed": true,
"checkedAt": "2026-09-20T09:30:00.000Z",
"latencyMs": 3
}{
"allowed": false,
"checkedAt": "2026-09-20T09:30:00.000Z",
"governance": true,
"reason": "GOVERNANCE_SUSPENSION",
"suspensionId": "susp_01HYX99..."
}WriteTuple
Create or delete a single relationship tuple. Use BulkWriteTuples for batch operations.
/nhs.rebac.v1.TupleService/WriteTuple{
"sub_tenant_id": "your-sub-tenant-id",
"operation": "OPERATION_CREATE",
"tuple": {
"subject": { "object": { "objectType": "user", "objectId": "dr-smith" } },
"relation": "treating_clinician",
"resource": { "objectType": "patient", "objectId": "patient-123" }
},
"idempotency_key": "optional-uuid-v4"
}{
"zeta_token": "GhUKEzE3MTk4NTcxODkyMzg1NTE4MjEYASAB"
}BulkWriteTuples
Write multiple relationship tuples in a single atomic request. Supports up to 1000 tuples per call.
/nhs.rebac.v1.TupleService/BulkWriteTuples{
"sub_tenant_id": "your-sub-tenant-id",
"tuples": [
{
"subject": { "object": { "objectType": "user", "objectId": "nurse-jones" } },
"relation": "ward_nurse",
"resource": { "objectType": "patient", "objectId": "patient-456" }
},
{
"subject": { "object": { "objectType": "user", "objectId": "nurse-jones" } },
"relation": "staff",
"resource": { "objectType": "ward", "objectId": "ward-a3" }
}
],
"idempotency_key": "optional-uuid-v4"
}{
"zeta_token": "GhUKEzE3MTk4NTcxODkyMzg1NTE4MjEYASAB"
}WriteSchema
Deploy an authorization schema to your sub-tenant. This replaces the existing schema version.
/nhs.rebac.v1.SchemaService/WriteSchema{
"sub_tenant_id": "your-sub-tenant-id",
"schema": "definition user {}\n\ndefinition patient {\n relation treating_clinician: user\n permission view_record = treating_clinician\n}"
}{
"zeta_token": "GhUKEzE3MTk4NTcxODkyMzg1NTE4MjEYASAB"
}DryRunSchema
Validate a schema without deploying it. Returns structured additions, removals, and breaking change warnings against the current schema.
/nhs.rebac.v1.SchemaService/DryRunSchema{
"sub_tenant_id": "your-sub-tenant-id",
"schema": "definition user {}\n\ndefinition patient {\n relation gp: user\n permission view_record = gp\n}"
}{
"additions": [
{ "object_type": "patient", "relation": "gp", "description": "New definition" }
],
"removals": [
{ "object_type": "patient", "relation": "treating_clinician", "description": "Removed definition" }
],
"breaking_changes": [
{ "object_type": "patient", "relation": "treating_clinician", "reason": "Removal may break existing tuples" }
]
}ReadSchema
Read the current active schema for your sub-tenant.
/nhs.rebac.v1.SchemaService/ReadSchema{ "sub_tenant_id": "your-sub-tenant-id" }{
"schema": "definition user {}\n\ndefinition patient {\n relation treating_clinician: user\n permission view_record = treating_clinician\n}",
"zeta_token": "GhUKEzE3MTk4NTcxODkyMzg1NTE4MjEYASAB"
}Schema Management (REST API)
Direct HTTP REST alternative to the gRPC SchemaService. Read current schemas, deploy new schema definitions, and perform pre-flight dry-runs without gRPC tooling.
GET /api/v1/tenant/schema # Read active schema definition & zeta_token
POST /api/v1/tenant/schema # Deploy schema text to tenant namespace
POST /api/v1/tenant/schema/dry-run # Validate schema syntax & compute structural diff
GET /api/v1/tenant/schema/templates # List vertical starter templates (Healthcare vs Enterprise)List Vertical Schema Templates
Retrieve pre-packaged, production-tested Zanzibar ReBAC schema starter templates. AuthHub provides specialized schemas for both Healthcare / NHS (Caldicott, break-glass, clinical care teams) and Enterprise B2B SaaS (hierarchical multi-tenancy, workspace delegation). If no custom schema has been uploaded, GET /api/v1/tenant/schema automatically serves the appropriate starter template based on your tenant's registered industry.
GET /api/v1/tenant/schema/templates{
"templates": [
{
"id": "healthcare-starter",
"name": "Healthcare & NHS Trust Starter",
"industry": "healthcare",
"description": "Patient records, clinical care teams, emergency break-glass, and Caldicott Guardian review",
"definitions": ["user", "patient", "organization", "team", "episode_of_care", "clinical_record"]
},
{
"id": "enterprise-starter",
"name": "Enterprise B2B SaaS Starter",
"industry": "enterprise",
"description": "Multi-tenant B2B hierarchy with workspaces, projects, documents, and service accounts",
"definitions": ["user", "tenant", "organization", "workspace", "project", "document", "service_account"]
}
]
}Read Active Schema
Fetch the current live schema definition and revision token for your isolated tenant namespace.
GET /api/v1/tenant/schema{
"schema": "definition user {}\n\ndefinition patient {\n relation treating_clinician: user\n permission view_record = treating_clinician\n}",
"zeta_token": "GhUKEzE3MTk4NTcxODkyMzg1NTE4MjEYASAB"
}Deploy Schema
Deploy a schema DSL text string. Automatically isolates definition names with your tenant namespace prefix.
POST /api/v1/tenant/schema{
"schema": "definition user {}\n\ndefinition patient {\n relation treating_clinician: user\n relation gp: user\n permission view_record = treating_clinician + gp\n}"
}{
"zeta_token": "GhUKEzE3MTk4NTcxODkyMzg1NTE4MjEYASAB"
}Dry-Run Schema Validation
Validate candidate schema syntax and detect breaking changes without applying changes to SpiceDB.
POST /api/v1/tenant/schema/dry-run{
"schema": "definition user {}\n\ndefinition patient {\n relation gp: user\n permission view_record = gp\n}"
}{
"additions": [
{ "object_type": "patient", "relation": "gp", "description": "New definition" }
],
"removals": [
{ "object_type": "patient", "relation": "treating_clinician", "description": "Removed definition" }
],
"breaking_changes": [
{ "object_type": "patient", "relation": "treating_clinician", "reason": "Removal may break existing tuples" }
]
}Write Tuple (REST)
Direct REST alternative to gRPC WriteTuple. Creates or updates a single relationship tuple with touch semantics.
POST /api/v1/tenant/tuples{
"objectType": "patient",
"objectId": "patient-123",
"relation": "treating_clinician",
"subjectType": "user",
"subjectId": "dr-smith"
}{
"objectType": "patient",
"objectId": "patient-123",
"relation": "treating_clinician",
"subjectType": "user",
"subjectId": "dr-smith",
"zetaToken": "GhUKEzE3MTk4NTcxODkyMzg1NTE4MjEYASAB",
"createdAt": "2026-09-20T09:30:00.000Z"
}Query Tuples (REST)
Query relationship tuples matching resource or subject filters with pagination.
GET /api/v1/tenant/tuples?sub_tenant_id={subTenantId}&object_type=patient&object_id=patient-123&relation=treating_clinician{
"tuples": [
{
"object_type": "patient",
"object_id": "patient-123",
"relation": "treating_clinician",
"subject_type": "user",
"subject_id": "dr-smith"
}
],
"total": 1
}Delete Tuples (REST)
Delete specific relationship tuples via the tenant REST API.
DELETE /api/v1/tenant/tuples{
"sub_tenant_id": "your-sub-tenant-id",
"object_type": "patient",
"object_id": "patient-123",
"relation": "treating_clinician",
"subject_type": "user",
"subject_id": "dr-smith"
}{
"status": "deleted",
"zeta_token": "GhUKEzE3MTk4NTcxODkyMzg1NTE4MjEYASAB"
}Expand Permission Tree (REST)
Expand the full relationship graph explaining why a permission is granted or denied for audit tracing and debugging.
POST /api/v1/tenant/permissions/expand{
"sub_tenant_id": "your-sub-tenant-id",
"object_type": "patient",
"object_id": "patient-123",
"permission": "view_record"
}{
"tree": {
"permission": "view_record",
"resource": { "objectType": "patient", "objectId": "patient-123" },
"expanded": [
{
"relation": "treating_clinician",
"subjects": [{ "objectType": "user", "objectId": "dr-smith" }]
}
]
}
}π‘ For reverse resource and subject search, see OpenID AuthZEN endpoints /authzen/v1/resource-search and /authzen/v1/subject-search.
Schema Lifecycle & Versioning (REST)
Full schema registry with staging promotions, structural diffing, syntax validation, and instant rollback.
POST /api/v1/tenant/schemas/versions/create # Create new draft version (with optional tag)
POST /api/v1/tenant/schemas/versions/list # List versions with stage, tag, and date filters
POST /api/v1/tenant/schemas/versions/get # Retrieve a specific schema version definition
POST /api/v1/tenant/schemas/versions/current # Retrieve current live schema version
POST /api/v1/tenant/schemas/diff # Compute structural diff between two versions
POST /api/v1/tenant/schemas/validate # Validate schema syntax & breaking changes
POST /api/v1/tenant/schemas/versions/promote # Promote draft to staging or live
POST /api/v1/tenant/schemas/versions/rollback # Rollback live schema to a prior version
POST /api/v1/tenant/schemas/rollback-targets # List safe rollback target versions// Request
{
"sub_tenant_id": "your-sub-tenant-id",
"source": "ver_01HYX3A...",
"target": "ver_01HYX5B...",
"include_breaking_changes": true
}
// Response (200 OK)
{
"additions": [
{ "object_type": "patient", "relation": "gp", "description": "New relation added" }
],
"removals": [
{ "object_type": "patient", "relation": "treating_clinician", "description": "Relation removed" }
],
"breaking_changes": [
{
"object_type": "patient",
"relation": "treating_clinician",
"reason": "Removing relation will invalidate existing tuples"
}
],
"safe": false
}// Request
{
"sub_tenant_id": "your-sub-tenant-id",
"tenant_id": "your-tenant-uuid",
"version_id": "ver_01HYX3A...",
"commit_message": "Emergency rollback due to clinical workflow regression",
"force": false
}
// Response (200 OK)
{
"rolled_back_to": "ver_01HYX3A...",
"status": "live",
"timestamp": "2026-09-19T20:45:00.000Z"
}Tamper-Evident Audit & Point-in-Time Verification
Verify cryptographic Merkle hash chains, reconstruct relationship graph topology at any past timestamp, and evaluate historic permission checks for legal and clinical audits.
GET /api/v1/tenant/audit?page=1&pageSize=20 # Paginated admin audit trail
GET /api/v1/tenant/audit/verify-chain?limit=1000 # Verify SHA-256 Merkle hash chain
GET /api/v1/tenant/audit/graph-at?timestamp=ISO8601 # Reconstruct graph at point in time
GET /api/v1/tenant/audit/permission-at?timestamp=ISO8601&subject=...&resource=...
GET /api/v1/tenant/authzen-audit?page=1&pageSize=50 # Query AuthZEN decision records// Query Parameters: ?page=1&pageSize=20&operationType=CHECK_PERMISSION&actor=dr-smith@trust.nhs.uk
// Response (200 OK)
{
"items": [
{
"entryId": "aud_01HYX3A99B87F12C",
"tenantId": "tenant-uuid-1",
"subTenantId": "sub-tenant-uuid",
"actorIdentity": "dr-smith@trust.nhs.uk",
"actorRole": "clinician",
"operationType": "CHECK_PERMISSION",
"affectedResource": "patient:patient-123",
"changeReason": "Routine clinical ward consultation",
"metadata": {
"clientIp": "10.42.0.15",
"userAgent": "EPR-Clinical-Client/2.4.0"
},
"createdAt": "2026-09-20T09:28:15.000Z"
}
],
"total": 142,
"page": 1,
"pageSize": 20
}// Response (200 OK)
{
"valid": true,
"entries_checked": 1000
}
// Response on Tamper Detected (200 OK with broken_at)
{
"valid": false,
"entries_checked": 450,
"broken_at": "evt_01HYX99..."
}// Request query: ?timestamp=2026-05-15T09:30:00Z&subject=user:dr-smith&resource=patient:pat-1001
// Response (200 OK)
{
"subject": "user:dr-smith",
"resource": "patient:pat-1001",
"timestamp": "2026-05-15T09:30:00.000Z",
"permitted": true,
"matched_relations": ["treating_clinician"],
"historical_graph_version": 142
}Error Handling β ReBAC Core
Common error responses for schema violations, invalid tuples, and rate limits.
{
"code": "failed_precondition",
"message": "relation 'nonexistent' not found in definition 'patient'"
}{
"code": "invalid_argument",
"message": "subject type 'unknown_type' is not defined in schema"
}{
"code": "rate_limited",
"message": "Quota exceeded: 1000 evaluations/month on Developer plan"
}{
"code": "permission_denied",
"message": "Object type 'other_tenant/patient' does not belong to namespace 'd41clw'"
}Group 2: AuthZEN (SARC Policy Evaluation)
AuthHub natively implements the OpenID AuthZEN 1.0 standard. Evaluate SARC policies without locking clients into proprietary authorization syntax.
Base path: /authzen/v1/ Β |Β Content-Type: application/authzen.v1+json or application/json
Evaluation
Evaluate an authorization request using the AuthZEN SARC model. Returns a boolean decision with optional context. Subject is overridden by the JWT sub claim to prevent spoofing.
POST /authzen/v1/evaluation{
"subject": { "type": "practitioner", "id": "P123" },
"action": { "name": "view_record" },
"resource": { "type": "patient", "id": "NHS_9876543210" },
"context": {
"agent": {
"type": "clinical_decision_support",
"id": "cds-agent-001"
}
}
}{ "decision": true }{
"decision": false,
"context": {
"reason": [{ "id": "no_relationship", "en": "No matching permission found" }]
}
}{
"decision": false,
"context": {
"reason": [{ "id": "break_glass_available", "en": "Emergency access available" }],
"obligations": [{
"id": "break_glass_escalation",
"type": "break_glass_request",
"uri": "/api/v1/break-glass",
"method": "POST",
"required_fields": ["justification", "authorising_clinician"],
"context": {
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"ttl_seconds": 300,
"requires_witness": true,
"max_duration_seconds": 3600
}
}]
}
}Headers: X-Tenant-ID (required), Authorization: Bearer <jwt> (subject override),x-coaz-mapping (optional β triggers COAZ payload transformation)
Break-Glass Configuration
Use Case: DSPT complianceConfigure which resource types and permissions are eligible for emergency break-glass access. When an eligible DENY is returned, clinicians receive an AARP obligation to request emergency access.
GET /api/v1/tenant/break-glass-config # List rules
POST /api/v1/tenant/break-glass-config # Add rule
DELETE /api/v1/tenant/break-glass-config/{id} # Delete rule{
"resourceType": "patient",
"permission": "view_record",
"eligible": true,
"ttlSeconds": 300,
"maxDurationS": 3600,
"requiresWitness": true,
"excludeAllPermissions": false,
"alertLevel": "elevated",
"justificationCategories": ["direct_care_emergency", "safeguarding_concern"]
}Alert Levels: standard (daily digest), elevated (immediate email to IG team), critical (SMS to Caldicott Guardian)
Justification Categories (DSPT): direct_care_emergency, safeguarding_concern, patient_consent, legal_order, public_health_emergency, life_threatening_situation
COAZ Mappings
Use Case: FHIR/HL7 translationDeclarative mapping schemas for translating external payloads (FHIR, HL7, custom) into AuthZEN SARC format. Activated mappings are used when clients send the x-coaz-mapping header.
GET /api/v1/tenant/coaz-mappings # List mappings
POST /api/v1/tenant/coaz-mappings # Upload new mapping
PUT /api/v1/tenant/coaz-mappings/{id}/activate # Activate version
POST /api/v1/tenant/coaz-mappings/{id}/dry-run # Test with sample
DELETE /api/v1/tenant/coaz-mappings/{id} # Delete (draft/deactivated only){
"name": "epic-fhir-r4",
"sourceSystem": "epic_fhir_r4",
"mappingJson": {
"version": "1.0",
"source_system": "epic_fhir_r4",
"mappings": {
"subject": { "type": { "default": "practitioner" }, "id": { "source": "jwt_sub" } },
"action": { "name": { "path": "$.intent", "transform": "fhir_intent" } },
"resource": { "type": { "path": "$.resourceType" }, "id": { "path": "$.id" } }
},
"transforms": { "fhir_intent": { "read": "view_record", "write": "edit_record" } }
}
}Lifecycle: Uploaded mappings start as draft. Activate to make live. Active mappings cannot be deleted β upload a new version and activate it instead. If error rate exceeds 5%/min, AuthHub auto-rollbacks to the previous version.
{
"payload": {
"resourceType": "Patient",
"id": "NHS_123",
"intent": "read",
"meta": { "source_system": "epic" }
}
}{
"success": true,
"sarc": {
"subject": { "type": "practitioner", "id": "P123" },
"action": { "name": "view_record" },
"resource": { "type": "patient", "id": "NHS_123" }
},
"executionMs": 1
}Resource Search
Find all resources of a given type that a subject has access to. Maps to SpiceDB LookupResources. Rate limited: 10 req/min per subject. 5-second timeout. Max 100 results per page.
POST /authzen/v1/resource-search{
"subject": { "type": "practitioner", "id": "P123" },
"action": { "name": "view_record" },
"resource_type": "patient",
"page_size": 50,
"cursor": ""
}{
"results": [
{ "type": "patient", "id": "NHS_111" },
{ "type": "patient", "id": "NHS_222" }
],
"total": 2,
"page_size": 50,
"next_cursor": null
}Subject Search
Find all subjects that have access to a specific resource. Maps to SpiceDB LookupSubjects. Requires service-to-service JWT with search:subject scope.
POST /authzen/v1/subject-search{
"resource": { "type": "patient", "id": "NHS_9876543210" },
"action": { "name": "view_record" },
"subject_type": "practitioner",
"page_size": 50
}{
"results": [
{ "type": "practitioner", "id": "P123" },
{ "type": "practitioner", "id": "P456" }
],
"total": 2,
"page_size": 50
}Batch Evaluations
Evaluate multiple AuthZEN SARC authorization requests in a single HTTP round-trip (boxcar pattern).
POST /authzen/v1/evaluations{
"evaluations": [
{
"subject": { "type": "practitioner", "id": "P123" },
"action": { "name": "view_record" },
"resource": { "type": "patient", "id": "NHS_9876543210" }
},
{
"subject": { "type": "practitioner", "id": "P123" },
"action": { "name": "edit_record" },
"resource": { "type": "patient", "id": "NHS_9876543210" }
}
]
}{
"evaluations": [
{ "decision": true },
{
"decision": false,
"context": {
"reason": [{ "id": "no_relationship", "en": "No matching permission found" }]
}
}
]
}Action Search
Reverse search to discover all actions a given subject is permitted to execute on a target resource.
POST /authzen/v1/action-search{
"subject": { "type": "practitioner", "id": "P123" },
"resource": { "type": "patient", "id": "NHS_9876543210" }
}{
"actions": [
"view_record",
"add_clinical_note"
]
}Agent Registry
Manage registered AI agents, autonomous callers, and tool runners with authorization constraints and ownership.
GET /api/v1/tenant/agent-registry # List registered agents
POST /api/v1/tenant/agent-registry # Register agent
PUT /api/v1/tenant/agent-registry/{id} # Update agent
DELETE /api/v1/tenant/agent-registry/{id} # Deregister agentEmergency Break-Glass Fulfilment
The public break-glass fulfillment endpoint advertised in AuthZEN DENY obligations. Submits emergency access justification and returns a tamper-evident audit record.
POST /api/v1/break-glass # Submit break-glass justification
GET /api/v1/break-glass/audit # Fetch hash-chained audit record (?event_id=...){
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"justification": "Cardiac arrest in Resus Bay 2; immediate record access required",
"authorising_clinician": "dr-smith@trust.nhs.uk",
"duration_seconds": 1800
}{
"status": "granted",
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"expires_at": "2026-09-19T21:15:00.000Z",
"audit_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}Error Handling β AuthZEN
Common error responses for AuthZEN evaluation failures.
{
"error": "invalid_request",
"error_description": "subject.type is required and must be a non-empty string"
}{
"error": "tenant_not_found",
"error_description": "No tenant found for X-Tenant-ID header value"
}{
"error": "mapping_failed",
"error_description": "COAZ mapping 'epic-fhir-r4' could not resolve action.name from path $.intent",
"mapping_id": "epic-fhir-r4"
}{
"error": "rate_limited",
"error_description": "Evaluation rate limit exceeded: 10000/month on Starter plan",
"retry_after": 3600
}Group 3: Lifecycle & Identity (SCIM 2.0)
RFC 7644 compliant. Drop-in compatible with Entra ID, Okta, and Ping Identity for automated user provisioning.
Users CRUD
Full SCIM 2.0 user lifecycle: create, list, filter, update, and delete provisioned users.
List & Filter Users
Server-side filtering and pagination. Supports eq, co, and sw operators.
GET /scim/v2/{connectionId}/Users?filter=userName eq "jane"&startIndex=1&count=50# Exact match
GET /scim/v2/{conn}/Users?filter=userName eq "jane.smith@org.com"
# Contains (case-insensitive)
GET /scim/v2/{conn}/Users?filter=displayName co "smith"
# Starts with
GET /scim/v2/{conn}/Users?filter=externalId sw "NHS-"
# With pagination (1-based startIndex)
GET /scim/v2/{conn}/Users?startIndex=51&count=50{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": 1250,
"itemsPerPage": 50,
"startIndex": 51,
"Resources": [
{ "id": "uuid", "userName": "jane.smith", "displayName": "Dr. Jane Smith", "active": true }
]
}Update User
RFC 7644 PATCH operations. Supports replace, add, and remove on userName, displayName, active, emails, and externalId.
PATCH /scim/v2/{connectionId}/Users/{userId}{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{ "op": "replace", "path": "displayName", "value": "Dr. Jane Smith" },
{ "op": "replace", "path": "active", "value": true }
]
}Delete User
Deactivate or permanently remove a provisioned user. Behaviour depends on connection's delete_mode setting (soft = deactivate, hard = remove).
DELETE /scim/v2/{connectionId}/Users/{userId}Soft delete (default): Returns 200 with active: false. Hard delete: Returns 204 No Content.
Groups CRUD
SCIM 2.0 group provisioning. Groups sync from your IdP and map to SpiceDB relations.
GET /scim/v2/{connectionId}/Groups # List groups
POST /scim/v2/{connectionId}/Groups # Create group
PATCH /scim/v2/{connectionId}/Groups/{groupId} # Update group membership
DELETE /scim/v2/{connectionId}/Groups/{groupId} # Delete group{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "Cardiology Consultants",
"members": [
{ "value": "user-uuid-1", "display": "Dr. Smith" },
{ "value": "user-uuid-2", "display": "Dr. Khan" }
]
}{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{ "op": "add", "path": "members", "value": [{ "value": "user-uuid-3" }] },
{ "op": "remove", "path": "members[value eq "user-uuid-1"]" }
]
}SCIM Connections Management
Provision, monitor, and decommission SCIM 2.0 endpoints for automated synchronization with enterprise identity providers (Microsoft Entra ID, Okta, PingFederate). Both canonical /api/v1/scim/{connId}/* and legacy /scim/v2/{connId}/* paths are accepted.
GET /api/v1/tenant/scim/connections # List all SCIM connections
POST /api/v1/tenant/scim/connections # Create new SCIM connection with bearer token
GET /api/v1/tenant/scim/connections/{id} # Inspect single connection details & state
DELETE /api/v1/tenant/scim/connections/{id} # Decommission / soft-delete connection
POST /api/v1/tenant/scim/connections/{id}/pause # Pause sync processing
POST /api/v1/tenant/scim/connections/{id}/resume # Resume sync processing
POST /api/v1/tenant/scim/connections/{id}/rotate-token # Rotate inbound bearer token with grace period
GET /api/v1/tenant/scim/connections/{id}/mappings # Inspect SCIM attribute-to-ReBAC mapping rules
GET /api/v1/tenant/scim/connections/{id}/log # Historical sync event audit log (paginated)
GET /api/v1/tenant/scim/connections/{id}/held-events # Deprovisioning guard quarantined events
POST /api/v1/tenant/scim/connections/{id}/held-events # Approve or discard held deprovisioning events
GET /api/v1/tenant/scim/dashboard # Aggregate sync telemetry, metrics, and error rateInspect SCIM Connection
Retrieve connection metadata, health status, synchronization timestamps, event counters, and IdP provider configuration.
GET /api/v1/tenant/scim/connections/{connectionId}{
"connectionId": "550e8400-e29b-41d4-a716-446655440000",
"name": "Microsoft Entra ID Production",
"providerType": "azure_ad",
"status": "active",
"healthState": "healthy",
"createdAt": "2026-04-01T12:00:00.000Z",
"updatedAt": "2026-09-20T08:00:00.000Z",
"lastSyncAt": "2026-09-20T08:15:22.000Z",
"eventsTotal": 14205,
"config": {
"deleteMode": "soft",
"deprovisioningThreshold": 10,
"rateLimitPerMin": 1000
}
}Decommission SCIM Connection
Soft-delete a SCIM connection. Immediately revokes bearer token authentication and prevents inbound provisioning requests without dropping audit history.
DELETE /api/v1/tenant/scim/connections/{connectionId}{
"deleted": true
}SCIM Mapping Rules Inspector
Inspect ordered attribute mapping rules that translate incoming SCIM user and group claims into Zanzibar relationship tuples in SpiceDB.
GET /api/v1/tenant/scim/connections/{connectionId}/mappings{
"rules": [
{
"ruleId": "rule_01HYX3A...",
"ruleType": "attribute_to_relation",
"sourcePath": "roles[primary eq true].value",
"matchCondition": "equals",
"matchValue": "Consultant",
"targetResourceType": "department",
"targetResourceIdTemplate": "dept-cardiology",
"targetRelation": "lead_consultant",
"subjectIsGroup": false,
"schemaValid": true,
"priority": 1,
"enabled": true
},
{
"ruleId": "rule_01HYX5B...",
"ruleType": "group_to_relation",
"groupNamePattern": "Clinical-Safety-Officers",
"targetResourceType": "system",
"targetResourceIdTemplate": "national-spine",
"targetRelation": "safety_auditor",
"subjectIsGroup": false,
"schemaValid": true,
"priority": 2,
"enabled": true
}
],
"total": 2
}SCIM Sync Event Log
Query historical synchronization events with pagination and filtering by event type (user_provision, user_deprovision, group_sync) and status (success, failed).
GET /api/v1/tenant/scim/connections/{connectionId}/log?page=1&pageSize=20&eventType=user_provision&status=success{
"items": [
{
"eventId": "evt_01HYX89A...",
"connectionId": "550e8400-e29b-41d4-a716-446655440000",
"eventType": "user_provision",
"status": "success",
"sourceIdentifier": "dr.smith@trust.nhs.uk",
"targetSubject": "user:dr-smith",
"details": "Successfully provisioned user and materialized treating_clinician tuple",
"processedAt": "2026-09-20T08:15:22.000Z",
"durationMs": 38
}
],
"total": 450,
"page": 1,
"pageSize": 20
}Deprovisioning Guard & Held Events Queue
To prevent catastrophic access lockouts caused by accidental IdP app unassignments, AuthHub automatically pauses the connection and holds deprovisioning events in quarantine when the deletion rate exceeds the configured threshold.
GET /api/v1/tenant/scim/connections/{id}/held-events # List quarantined deprovisioning events
POST /api/v1/tenant/scim/connections/{id}/held-events # Approve or discard quarantined events// Request: GET /api/v1/tenant/scim/connections/550e8400-e29b-41d4-a716-446655440000/held-events
// Response (200 OK)
{
"items": [
{
"eventId": "evt_held_01HYX...",
"eventType": "user_deprovision",
"sourceIdentifier": "dr-smith@trust.nhs.uk",
"heldAt": "2026-09-20T08:30:00.000Z",
"decision": "pending"
}
],
"total": 1
}// Request: POST /api/v1/tenant/scim/connections/550e8400-e29b-41d4-a716-446655440000/held-events
// Headers: X-User-Identity: security-officer@trust.nhs.uk
{
"decision": "approve" // or "discard"
}
// Response (200 OK)
{
"approved": true,
"count": 1,
"connectionStatus": "active"
}Usage & Quota
Monitor current resource consumption against plan limits.
Get Usage Snapshot
GET /api/v1/tenant/usage{
"evaluations_this_month": 4523,
"evaluations_limit": 10000,
"tuples_used": 847,
"tuples_limit": 1000,
"sub_tenants_count": 1,
"sub_tenants_limit": 1,
"plan": "developer",
"billing_period_start": "2026-06-01T00:00:00.000Z",
"billing_period_end": "2026-06-30T23:59:59.999Z"
}GetQuotaUsage (gRPC)
/nhs.rebac.v1.ProvisioningService/GetQuotaUsage{ "sub_tenant_id": "your-sub-tenant-id" }{
"tuple_count": 4523,
"tuple_quota": 100000,
"usage_percentage": 4.52,
"alert_threshold_reached": false
}Sub-Tenant Lifecycle
Provision, update, and delete isolated sub-tenant namespaces.
/nhs.rebac.v1.ProvisioningService/ProvisionSubTenant{
"name": "patient-portal-prod",
"description": "Production environment for patient portal",
"quotaProfile": "standard"
}{
"subTenantId": "st_01HYX3...",
"namespace": "tenant_abc/patient-portal-prod",
"createdAt": "2024-06-01T10:30:00Z"
}Error Handling β SCIM & Lifecycle
Common error responses for SCIM provisioning and lifecycle operations.
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status": "409",
"detail": "User with userName 'jane.smith@org.com' already exists"
}{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status": "400",
"detail": "Unsupported filter operator 'gt' on attribute 'userName'"
}{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status": "404",
"detail": "SCIM connection 'conn-xyz' not found or not active"
}{
"code": "quota_exceeded",
"message": "Sub-tenant limit reached: 1/1 on Developer plan. Upgrade to Starter for 3 sub-tenants."
}Group 4: Workload Identity Federation & OAuth 2.0 (RFC 8693)
RFC 8693 Token Exchange, OAuth 2.0 authorization, and Identity Assertion-to-JWT / ID-JAG bridging. Eliminates static API keys for machine workloads, CI/CD pipelines, Kubernetes pods, and autonomous AI agents.
OAuth 2.0 Authorization Endpoint
Standard interactive authorization endpoint compliant with RFC 6749 and RFC 7636 (PKCE with code_challenge_method=S256). Redirects authenticated browser or agent sessions back to the client application with an authorization code, or presents IdP federation instructions.
GET /oauth/authorize?response_type=code&client_id=<client_id>&redirect_uri=<uri>&scope=openid+profile&state=<state>&code_challenge=<challenge>&code_challenge_method=S256HTTP/1.1 302 Found
Location: https://client.app/callback?code=authhub-code-61757468687562...&state=xyz123Token Exchange & Client Authentication
Exchange external OIDC tokens, IdP ID tokens, or CI/CD assertions for short-lived, audience-bound AuthHub access tokens. Supports RFC 6749 Β§2.3.1 client_secret_basic (HTTP Basic Auth header), client_secret_post, and private_key_jwt. If an explicit X-Tenant-ID is omitted, AuthHub automatically defaults to the tenant configured for the client credentials.
POST /oauth/tokenStandard Workload Token Exchange
POST /oauth/token HTTP/1.1
Host: api.authhub.cloud
Authorization: Basic <base64(client_id:client_secret)>
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
&subject_token_type=urn:ietf:params:oauth:token-type:jwt
&audience=https://api.authhub.cloud/mcp
&scope=schema:write tuple:read{
"access_token": "eyJhbGciOiJSUzI1NiJ9...",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 900,
"scope": "schema:write tuple:read"
}ID-JAG Grant Profile (Identity Assertion to JWT)
Enterprise Managed Authorization profile for agentic platforms and federated enterprise IdPs (e.g. Okta, Entra ID, Ping). Exchanges an external IdP ID Token for an audience-bound AuthHub assertion token targeting the MCP Gateway.
POST /oauth/token HTTP/1.1
Host: api.authhub.cloud
Authorization: Basic <base64(client_id:client_secret)>
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&requested_token_type=urn:ietf:params:oauth:token-type:id-jag
&subject_token=<okta_or_entra_id_token>
&subject_token_type=urn:ietf:params:oauth:token-type:id_token
&audience=https://api.authhub.cloud/mcp{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImF1dGhodWItc2lnLTIwMjZhIn0...",
"issued_token_type": "urn:ietf:params:oauth:token-type:id-jag",
"token_type": "Bearer",
"expires_in": 900,
"scope": "authhub:authorize"
}{
"dry_run": true,
"would_succeed": true,
"workload_id": "wkld_7f3a2b1c4d5e",
"resolved_scopes": ["schema:write", "tuple:read"],
"token_lifetime_seconds": 900,
"issuer_id": "abc123-def456",
"claim_mappings_applied": { "repo": "nhs-digital/auth-service" }
}GitHub Actions Workflow
Complete workflow for exchanging a GitHub OIDC token and deploying a schema β zero secrets stored.
name: Deploy Schema
on:
push:
branches: [main]
paths: ['schemas/**']
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get AuthHub token via WIF
id: authhub
run: |
# Request GitHub OIDC token
SUBJECT_TOKEN=$(curl -sS \
-H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=authhub.nhs.uk" | jq -r '.value')
# Exchange for AuthHub access token
RESPONSE=$(curl -sS -X POST https://authhub.nhs.uk/oauth/token \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=$SUBJECT_TOKEN" \
-d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \
-d "scope=schema:write")
echo "token=$(echo $RESPONSE | jq -r '.access_token')" >> $GITHUB_OUTPUT
- name: Deploy schema
run: |
curl -X POST https://authhub.nhs.uk/nhs.rebac.v1.SchemaService/WriteSchema \
-H "Authorization: Bearer ${{ steps.authhub.outputs.token }}" \
-H "Content-Type: application/json" \
-d @schemas/patient-records.jsonToken Introspection
RFC 7662 β verify whether an issued workload token is still active (not expired, not revoked).
POST /oauth/introspecttoken=eyJhbGciOiJSUzI1NiJ9...{
"active": true,
"sub": "wkld_7f3a2b1c4d5e",
"scope": "schema:write tuple:read",
"exp": 1720800902,
"token_type": "Bearer",
"tenant_id": "leeds-teaching-hospitals",
"workload_id": "wkld_7f3a2b1c4d5e"
}{ "active": false }Issuer Management
Register and manage trusted OIDC issuers whose tokens your workloads can exchange. Requires Admin JWT.
POST /api/v1/tenants/:tenantId/issuers # Register issuer
GET /api/v1/tenants/:tenantId/issuers # List issuers
GET /api/v1/tenants/:tenantId/issuers/:id # Get issuer
PATCH /api/v1/tenants/:tenantId/issuers/:id # Update issuer
DELETE /api/v1/tenants/:tenantId/issuers/:id # Delete issuer{
"display_name": "GitHub Actions (production)",
"issuer_url": "https://token.actions.githubusercontent.com",
"allowed_audiences": ["authhub.nhs.uk"],
"max_token_lifetime_seconds": 900,
"auto_provision_enabled": true,
"auto_provision_daily_limit": 50,
"rate_limit_per_minute": 300,
"scope_restrictions": ["schema:*", "tuple:read"],
"claim_mappings": [
{
"mapping_type": "direct",
"source_claim": "repository",
"target_attribute": "repo",
"required": true,
"transform": "none"
}
],
"claim_filters": [
{ "claim": "repository_owner", "operator": "equals", "value": "nhs-digital" },
{ "claim": "ref", "operator": "matches_glob", "value": "refs/heads/main" }
]
}Workload Identity Management
View, manage, and revoke machine identities created through federation.
GET /api/v1/tenants/:tenantId/workload-identities # List
GET /api/v1/tenants/:tenantId/workload-identities/:id # Get
DELETE /api/v1/tenants/:tenantId/workload-identities/:id # Delete
POST /api/v1/tenants/:tenantId/workload-identities/:id/revoke # Revoke
POST /api/v1/tenants/:tenantId/workload-identities/bulk-import # Bulk import{
"reason": "Compromised CI pipeline token",
"revoked_by": "admin@leeds-trust.nhs.uk"
}{
"identities": [
{
"external_issuer_id": "abc123-def456",
"external_subject": "system:serviceaccount:prod:schema-migrator",
"display_name": "Schema Migrator (prod)",
"default_scopes": ["schema:write"]
}
]
}Error Handling β Workload Identity
Common error responses for token exchange and workload identity operations.
{
"error": "invalid_grant",
"error_description": "Token signature verification failed: untrusted issuer 'https://evil.example.com'"
}{
"error": "insufficient_scope",
"error_description": "Requested scope 'admin:delete' denied by FGA policy for workload wkld_7f3a2b1c4d5e"
}{
"error": "unknown_workload",
"error_description": "No workload identity found for subject 'repo:evil-org/hack:ref:refs/heads/main'. Auto-provision is disabled for this issuer."
}{
"error": "plan_restriction",
"error_description": "Workload Identity Federation requires Professional or Enterprise plan. Current plan: Starter."
}{
"error": "rate_limited",
"error_description": "Token exchange rate limit exceeded: 300/min for issuer 'github-actions-prod'",
"retry_after": 12
}Tenant Analytics & Telemetry API
High-resolution operational metrics, time-series evaluation telemetry, latency percentiles, access density heatmaps, and rate-limited CSV reporting for tenant monitoring and compliance audits.
Base path: /api/v1/tenant/analytics/ Β |Β Auth: Bearer Token (analytics:read or admin) Β |Β Freshness Header: X-Data-Freshness: <seconds>
Platform Overview Summary
Retrieve instantaneous platform health, rolling 24-hour evaluation aggregates, percentile latencies, and cache hit metrics.
GET /api/v1/tenant/analytics/overview{
"data_freshness_seconds": 3,
"time_window": {
"from": "2026-09-19T00:00:00.000Z",
"to": "2026-09-20T00:00:00.000Z"
},
"totals_24h": {
"total_evaluations": 1482930,
"allow_count": 1421040,
"deny_count": 61420,
"break_glass_count": 470,
"unique_subjects": 3410,
"unique_resources": 89450
},
"latency": {
"p50_ms": 0.84,
"p90_ms": 1.72,
"p99_ms": 4.15
},
"cache": {
"hit_rate_pct": 94.6,
"l1_hits": 1184000,
"l2_hits": 219000,
"misses": 79930
}
}Time-Series Evaluations
Query time-sliced authorization decision counts. Supports pagination cursors and multiple granularities (minute, hour, day).
GET /api/v1/tenant/analytics/evaluations?from=2026-09-19T00:00:00Z&to=2026-09-20T00:00:00Z&granularity=hour&page=1&pageSize=50{
"items": [
{
"timestamp": "2026-09-19T14:00:00.000Z",
"total": 64210,
"allow": 61890,
"deny": 2300,
"break_glass": 20
},
{
"timestamp": "2026-09-19T15:00:00.000Z",
"total": 71040,
"allow": 68420,
"deny": 2605,
"break_glass": 15
}
],
"pagination": {
"page": 1,
"page_size": 50,
"total_items": 24,
"total_pages": 1
}
}Latency SLOs & Error Telemetry
Track latency percentiles (p50, p90, p99) against contractual NHS SLOs and inspect error distribution by error code.
GET /api/v1/tenant/analytics/latency?from=2026-09-19T00:00:00Z&to=2026-09-20T00:00:00Z&granularity=hour
GET /api/v1/tenant/analytics/errors?from=2026-09-19T00:00:00Z&to=2026-09-20T00:00:00Z&granularity=hour{
"items": [
{
"timestamp": "2026-09-19T14:00:00.000Z",
"p50_ms": 0.81,
"p90_ms": 1.65,
"p99_ms": 3.92,
"avg_ms": 0.94,
"min_ms": 0.32,
"max_ms": 14.2
}
]
}{
"items": [
{
"timestamp": "2026-09-19T14:00:00.000Z",
"error_count": 12,
"by_code": {
"SCHEMA_NOT_FOUND": 0,
"DISALLOWED_RELATION": 9,
"EVALUATION_TIMEOUT": 1,
"RATE_LIMIT_EXCEEDED": 2
}
}
]
}Domain Dimension Rollups
Pre-aggregated dimensional metrics across autonomous agents, SCIM synchronization pipelines, relationship tuple churn, and schema compilation.
GET /api/v1/tenant/analytics/agents # AI agent authorization query volume & approval rates
GET /api/v1/tenant/analytics/break-glass # Emergency access frequency, durations & audit statuses
GET /api/v1/tenant/analytics/tuples # Relationship churn rate (writes, deletes, net change)
GET /api/v1/tenant/analytics/schema # Schema compilation benchmarks & deployment history
GET /api/v1/tenant/analytics/scim # SCIM inbound sync volume, operations & latency{
"items": [
{
"agent_id": "agnt_01HYX5K3...",
"agent_type": "clinical_decision_support",
"display_name": "Oncology CDS Agent v2",
"total_evaluations": 14280,
"allow_rate": 0.984,
"tool_invocations": 3910,
"last_active": "2026-09-19T23:54:12.000Z"
}
]
}Interactive Analytics & Stale Permission Pruning
Access temporal density matrices for workload scheduling, 7-day rolling trendlines, top subject/resource leaderboards, and identify dormant tuples for least-privilege compliance.
GET /api/v1/tenant/analytics/heatmap?startDate=2026-09-01&endDate=2026-09-20&page=1&pageSize=20
GET /api/v1/tenant/analytics/trends?granularity=daily
GET /api/v1/tenant/analytics/leaderboard?limit=10
GET /api/v1/tenant/analytics/last-used?staleThresholdDays=30{
"stale_threshold_days": 30,
"stale_tuples_count": 14,
"items": [
{
"subject": "user:temp-locum-042",
"relation": "treating_clinician",
"resource": "patient:NHS_9876543210",
"last_exercised": "2026-07-14T10:12:00.000Z",
"inactive_days": 67,
"recommended_action": "revoke"
}
]
}Rate-Limited CSV Export
Stream raw or aggregated metrics for external SIEM, PowerBI, or cold-storage archiving. Strictly rate-limited to 3 exports per hour per tenant.
GET /api/v1/tenant/analytics/evaluations/export?from=2026-09-01T00:00:00Z&to=2026-09-20T00:00:00Z&granularity=dailytimestamp,total_evaluations,allow_count,deny_count,break_glass_count
2026-09-01T00:00:00Z,1245000,1198000,46600,400
2026-09-02T00:00:00Z,1310000,1260000,49550,450
...Error Handling β Analytics & Telemetry
Standard error responses for invalid analytics queries and export throttling.
{
"error": "invalid_time_range",
"message": "Parameter 'from' (2026-09-21) must precede 'to' (2026-09-20)"
}{
"error": "rate_limited",
"message": "Export rate limit exceeded: maximum 3 exports per hour per tenant",
"retry_after": 1420
}AI Agent Registry & Model Context Protocol (MCP) Edge Gateway
Administer autonomous AI agents, register clinical decision support (CDS) callers, and enforce runtime authorization gating over the Model Context Protocol (MCP) Edge Gateway. Features stateless server/discover pre-auth capability discovery, RFC 9728 401 challenges, dynamic tools/list entitlement filtering, and sub-5ms AuthZEN SARC policy evaluations on tools/call.
Endpoints: /api/v1/tenant/agent-registry Β |Β POST /mcp Β |Β POST / Β |Β MCP Spec Support: 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26 Β |Β Auth: RFC 8707 Audience-Bound JWT (https://api.authhub.cloud/mcp)
Agent Registration Lifecycle
Create, query, approve, update, and deregister autonomous agents with explicit allowed actions and governance attestations.
GET /api/v1/tenant/agent-registry # List registered agents
POST /api/v1/tenant/agent-registry # Register agent (returns 201 Created)
GET /api/v1/tenant/agent-registry/{id} # Get agent details
PUT /api/v1/tenant/agent-registry/{id} # Update parameters, actions, or approval
DELETE /api/v1/tenant/agent-registry/{id} # Deregister agent (immediate revocation)Register Agent
// Request Body
{
"agentType": "clinical_decision_support",
"agentExtId": "cds-oncology-agent-v2",
"displayName": "Oncology CDS Agent v2",
"allowedActions": [
"read_diagnostic_report",
"suggest_pathway",
"view_patient_summary"
]
}
// Response (201 Created)
{
"agent_id": "agnt_01HYX5K3M8PQRS91",
"agent_type": "clinical_decision_support",
"agent_ext_id": "cds-oncology-agent-v2",
"display_name": "Oncology CDS Agent v2",
"allowed_actions": [
"read_diagnostic_report",
"suggest_pathway",
"view_patient_summary"
],
"approved": false,
"approved_by": null,
"approved_at": null,
"revoked_at": null,
"created_at": "2026-09-20T00:10:00.000Z"
}Approve & Update Agent
Agents remain in pending_approval until a clinical lead or administrator approves them.
// Request Body
{
"displayName": "Oncology CDS Agent v2 (Production)",
"allowedActions": [
"read_diagnostic_report",
"suggest_pathway",
"view_patient_summary"
],
"approved": true,
"approvedBy": "dr-watson@trust.nhs.uk",
"revoked": false
}
// Response (200 OK)
{
"agent_id": "agnt_01HYX5K3M8PQRS91",
"approved": true,
"approved_by": "dr-watson@trust.nhs.uk",
"approved_at": "2026-09-20T00:12:00.000Z",
"revoked_at": null
}Runtime Enforcement & MCP Tool Attestation
When an AI agent or model calls POST /authzen/v1/evaluation, pass the agent identity in the request context. AuthHub checks the registry in real-time: if unapproved, revoked, or if the requested action is not in allowed_actions, evaluation returns DENY.
// POST /authzen/v1/evaluation
{
"subject": { "type": "agent", "id": "cds-oncology-agent-v2" },
"action": { "name": "read_diagnostic_report" },
"resource": { "type": "patient", "id": "NHS_9876543210" },
"context": {
"agent": {
"id": "agnt_01HYX5K3M8PQRS91",
"type": "clinical_decision_support",
"tool_name": "mcp_fetch_oncology_report"
}
}
}
// Response (200 OK - Permit)
{
"decision": true,
"context": {
"agent_validated": true,
"approval_verified": true
}
}Error Handling β AI Agent Registry
Common status codes for agent registration and runtime gating.
{
"decision": false,
"context": {
"reason": [
{
"id": "agent_not_approved",
"en": "Agent 'agnt_01HYX5K3M8PQRS91' is pending clinical supervisor approval"
}
]
}
}{
"decision": false,
"context": {
"reason": [
{
"id": "action_not_allowed_for_agent",
"en": "Action 'prescribe_chemotherapy' is not permitted in agent registry profile"
}
]
}
}Model Context Protocol (MCP) Edge Gateway
Inline Policy Enforcement Point (PEP) governing autonomous AI agents and LLM tool execution over JSON-RPC 2.0. Protects upstream tool fleets (order execution, clinical FHIR APIs, SQL runners) by negotiating protocol versions, validating RFC 8707 audience-bound tokens, dynamically pruning tools/list to authorized capabilities, and evaluating AuthZEN SARC policies inline with sub-5ms latency.
POST /mcp # Canonical MCP JSON-RPC 2.0 transport endpoint
POST / # Root-path MCP gateway fallback routingSupported MCP Protocol Versions
1. Stateless Pre-Auth Discovery (server/discover)
Per the MCP 2026-07-28 specification, clients (e.g. Ballerina MCP Inspector, Claude Desktop) query server/discover before authenticating to discover supported versions, gateway metadata, and operational constraints without presenting credentials.
{
"jsonrpc": "2.0",
"id": 1,
"method": "server/discover"
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"supportedVersions": ["2026-07-28", "2025-11-25", "2025-06-18", "2025-03-26"],
"capabilities": {
"tools": {},
"resources": {},
"prompts": {}
},
"serverInfo": {
"name": "authhub-mcp-gateway",
"version": "0.1.0"
},
"_meta": {
"io.modelcontextprotocol/serverInfo": {
"name": "authhub-mcp-gateway",
"version": "0.1.0"
}
},
"instructions": "Authorization gateway. Tokens must be issued for this resource (audience-bound). Tool/resource/prompt access is authorized per call and may be revoked in real time by governance."
}
}2. RFC 9728 401 Challenge Handshake
When an unauthenticated caller executes protected methods (e.g. tools/list or tools/call), the gateway responds with HTTP 401 emitting the RFC 9728 resource_metadata pointer. If an expired or invalid token was presented, RFC 6750 error="invalid_token" parameters are attached.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="AuthHub", resource_metadata="https://api.authhub.cloud/.well-known/oauth-protected-resource"
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "Authorization required"
}
}HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token", error_description="jwt expired", realm="AuthHub", resource_metadata="https://api.authhub.cloud/.well-known/oauth-protected-resource"
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "jwt expired"
}
}3. Protocol Lifecycle Negotiation (initialize)
Authenticated agents initiate sessions by declaring their protocol version and client info. The gateway negotiates the highest mutually supported version.
// Headers: Authorization: Bearer <authhub_access_token>
{
"jsonrpc": "2.0",
"id": 2,
"method": "initialize",
"params": {
"protocolVersion": "2026-07-28",
"capabilities": {},
"clientInfo": {
"name": "enterprise-agent-runner",
"version": "2.4.0"
}
}
}{
"jsonrpc": "2.0",
"id": 2,
"result": {
"protocolVersion": "2026-07-28",
"capabilities": {
"tools": {},
"resources": {},
"prompts": {}
},
"serverInfo": {
"name": "authhub-mcp-gateway",
"version": "0.1.0"
}
}
}4. Entitlement-Filtered Tool Discovery (tools/list)
Unlike native unmanaged MCP servers that reveal all backend capabilities to LLMs, AuthHub filters the catalog returned by tools/list so that agents only see tools they hold baseline authorization to invoke.
// Headers: Authorization: Bearer <authhub_access_token>
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/list"
}5. Inline Gating & Circuit Breaker (tools/call)
Every tool execution is intercepted and evaluated against the tenant's active ReBAC schema and real-time governance signals. Denied calls are rejected with JSON-RPC error code -32003 and anchored into the immutable SHA-256 Merkle audit trail.
// Headers: Authorization: Bearer <authhub_access_token>
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "query_database",
"arguments": {
"sql": "SELECT patient_id, diagnosis FROM clinical_records WHERE trust_id = 'trust-042'",
"connection_id": "8f700688-46fb-40a2-a05e-a6119f6f6004"
}
}
}Context-Aware Authorization Mappings (COAZ)
Declarative mapping pipeline translating complex clinical payloads (such as Epic/Cerner HL7 FHIR R4 resources, IP CIDRs, device posture scores, and temporal attributes) into canonical SpiceDB relationships without modifying client code.
Base path: /api/v1/tenant/coaz-mappings Β |Β Auth: Bearer Token (policy:manage or admin) Β |Β Runtime Header: X-COAZ-Mapping: <mapping_name_or_id>
Mapping Rules Management
Create, list, version, activate, and delete JSONPath-based context transformation schemas.
GET /api/v1/tenant/coaz-mappings # List configured mapping rules
POST /api/v1/tenant/coaz-mappings # Create new mapping rule
PUT /api/v1/tenant/coaz-mappings/{id}/activate # Activate version & flush edge cache
DELETE /api/v1/tenant/coaz-mappings/{id} # Delete mapping ruleCreate Mapping Rule
// Request Body
{
"name": "fhir-r4-encounter-to-spicedb",
"sourceSystem": "epic_fhir_r4",
"mappingJson": {
"subject": {
"type": "practitioner",
"id": "$.practitioner.identifier[?(@.system=='https://fhir.nhs.uk/Id/sds-user-id')].value"
},
"action": {
"name": "$.action == 'read' ? 'view_record' : 'update_record'"
},
"resource": {
"type": "patient",
"id": "$.resource.subject.reference.replace('Patient/', '')"
},
"conditions": [
{
"claim": "$.context.ip_address",
"operator": "in_cidr",
"value": "10.0.0.0/8"
},
{
"claim": "$.context.device_trust_score",
"operator": "gte",
"value": 80
}
]
},
"createdBy": "secops@trust.nhs.uk"
}
// Response (201 Created)
{
"mapping_id": "map_01HYX99K4M3N",
"name": "fhir-r4-encounter-to-spicedb",
"version": 1,
"is_active": false,
"created_at": "2026-09-20T00:15:00.000Z"
}Dry-Run Transformation Simulator
Verify and debug mapping transformations against sample clinical inputs prior to deployment without touching live authorization databases.
// Request Body (Raw FHIR payload sample)
{
"input_payload": {
"practitioner": {
"identifier": [{ "system": "https://fhir.nhs.uk/Id/sds-user-id", "value": "P12345" }]
},
"action": "read",
"resource": {
"subject": { "reference": "Patient/NHS-9876543210" }
},
"context": {
"ip_address": "10.14.2.19",
"device_trust_score": 95
}
}
}
// Response (200 OK)
{
"evaluated": true,
"resolved_subject": { "type": "practitioner", "id": "P12345" },
"resolved_action": "view_record",
"resolved_resource": { "type": "patient", "id": "NHS-9876543210" },
"conditions_matched": true,
"simulated_spicedb_check": "practitioner:P12345#view_record@patient:NHS-9876543210",
"execution_duration_ms": 0.42
}Error Handling β COAZ Mappings
Error statuses for invalid mapping syntax and resolution faults.
{
"error": "invalid_mapping_syntax",
"message": "JSONPath syntax error at '$.practitioner.identifier[?(@.system==': unclosed predicate"
}{
"error": "coaz_resolution_failed",
"mapping_id": "map_01HYX99K4M3N",
"message": "Failed to extract required resource ID: path '$.resource.subject.reference' returned null"
}Webhooks & Dead-Letter Queue (DLQ) API
Real-time event subscriptions with HMAC-SHA256 signature verification, automated exponential backoff retries, and a durable Dead-Letter Queue for audit logging and SIEM integration.
Base path: /api/v1/tenant/webhooks Β |Β Auth: Bearer Token (webhook:manage or admin) Β |Β Signature Header: X-AuthHub-Signature-256: t=<timestamp>,v1=<hex_hmac>
Subscription Lifecycle
Subscribe HTTPS endpoints to system events (tuple.written, tuple.deleted, schema.deployed, break_glass.invoked, governance.circuit_breaker).
GET /api/v1/tenant/webhooks # List configured subscriptions
POST /api/v1/tenant/webhooks # Create new subscription
GET /api/v1/tenant/webhooks/{id} # Get subscription details
PUT /api/v1/tenant/webhooks/{id} # Update URL, events, or headers
DELETE /api/v1/tenant/webhooks/{id} # Delete subscription (204 No Content)
POST /api/v1/tenant/webhooks/{id}/test # Dispatch ping verification event
POST /api/v1/tenant/webhooks/{id}/rotate-secret # Rotate HMAC secret with 24h grace periodCreate Subscription
// Request Body
{
"url": "https://siem.trust.nhs.uk/authhub-events",
"eventTypes": [
"tuple.written",
"tuple.deleted",
"break_glass.invoked",
"schema.deployed"
],
"secret": "whsec_9f83a2b1c4d5e6f7a8b9c0d1e2f3a4b5",
"customHeaders": {
"X-Trust-Cluster": "uksouth-prod-01"
}
}
// Response (201 Created)
{
"id": "wh_01HYX3A9B2C4D6E8",
"url": "https://siem.trust.nhs.uk/authhub-events",
"event_types": ["tuple.written", "tuple.deleted", "break_glass.invoked", "schema.deployed"],
"status": "active",
"secret_prefix": "whsec_9f83...",
"created_at": "2026-09-20T00:18:00.000Z"
}Dead-Letter Queue (DLQ) & Replay
When deliveries fail after 5 exponential retry attempts, messages are moved to the Dead-Letter Queue. Inspect failure diagnostics and replay failed events individually or in bulk.
GET /api/v1/tenant/webhooks/dead-letter # List failed deliveries with root causes
POST /api/v1/tenant/webhooks/dead-letter/{deliveryId}/retry # Replay single event
POST /api/v1/tenant/webhooks/dead-letter/bulk-retry # Replay all queued DLQ items{
"items": [
{
"delivery_id": "del_01HYX99QRS12",
"webhook_id": "wh_01HYX3A9B2C4D6E8",
"event_type": "break_glass.invoked",
"target_url": "https://siem.trust.nhs.uk/authhub-events",
"attempts": 5,
"last_attempt_at": "2026-09-19T22:45:00.000Z",
"error_code": "HTTP_503_SERVICE_UNAVAILABLE",
"last_error": "Server returned 503: upstream rate limiter",
"payload_preview": { "event_id": "550e8400-...", "justification": "Cardiac arrest" }
}
],
"total_dead_letter_items": 1
}HMAC-SHA256 Signature Verification
All webhook dispatches include the X-AuthHub-Signature-256 header to prevent spoofing and replay attacks.
import * as crypto from 'crypto';
function verifyWebhookSignature(payload: string, header: string, secret: string): boolean {
// header format: "t=1726789200,v1=9f83a2b1..."
const parts = Object.fromEntries(header.split(',').map(kv => kv.split('=')));
const timestamp = parts['t'];
const signature = parts['v1'];
// Prevent replay attacks (reject payloads older than 5 minutes)
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const signedPayload = `${timestamp}.${payload}`;
const expectedSig = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSig));
}Break-Glass Emergency Access REST API
Clinical emergency override protocol fulfilling AuthZEN obligations. Grants time-bounded, emergency access during life-or-death situations with mandatory clinical justification, Caldicott Guardian audit tracking, and tamper-evident Merkle hash verification.
Fulfillment Endpoint: POST /api/v1/break-glass Β |Β Tenant Policy Config: /api/v1/tenant/break-glass-config Β |Β Audit & Merkle: GET /api/v1/break-glass/audit
Emergency Policy Configuration
Configure maximum duration bounds, witness clinician requirements, notification webhook triggers, and resource restrictions.
GET /api/v1/tenant/break-glass-config # Inspect current tenant emergency policy
POST /api/v1/tenant/break-glass-config # Update emergency policy parameters{
"tenant_id": "trust-042",
"max_duration_seconds": 3600,
"default_duration_seconds": 1800,
"requires_witness_clinician": false,
"permitted_resource_types": ["patient", "episode_of_care"],
"mandatory_justification_min_chars": 20,
"notification_webhooks": ["wh_01HYX3A9B2C4D6E8"]
}Execute Emergency Override (Fulfillment)
Submit clinical justification to activate emergency access. Dispatches real-time webhook notifications and appends a cryptographically signed leaf to the tenant Merkle audit tree.
POST /api/v1/break-glass{
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"justification": "Emergency department cardiac resuscitation; patient record required immediately",
"authorising_clinician": "dr-smith@trust.nhs.uk",
"duration_seconds": 1800,
"witness_clinician": "nurse-jones@trust.nhs.uk"
}{
"status": "granted",
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"granted_at": "2026-09-20T00:20:00.000Z",
"expires_at": "2026-09-20T00:50:00.000Z",
"duration_seconds": 1800,
"authorising_clinician": "dr-smith@trust.nhs.uk",
"audit_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"merkle_leaf_index": 4821
}Tamper-Evident Merkle & RFC 3161 Audit
Verify non-repudiation of emergency access events using SHA-256 Merkle chain verification and RFC 3161 trusted timestamp tokens.
GET /api/v1/break-glass/audit?event_id=550e8400-e29b-41d4-a716-446655440000{
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"valid": true,
"audit_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"merkle_root": "7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069",
"merkle_leaf_index": 4821,
"tsa_timestamp": "2026-09-20T00:20:00.412Z",
"tsa_signature_verified": true
}Active Sessions & Early Revocation
List live emergency sessions and support early revocation by Caldicott Guardians or SIEM automated containment systems.
GET /api/v1/tenant/break-glass/active # List active emergency overrides
POST /api/v1/tenant/break-glass/{id}/revoke # Terminate session before TTL expires
POST /api/v1/tenant/break-glass/{id}/extend # Extend emergency session within ceiling// Request Body
{
"revoked_by": "caldicott_guardian@trust.nhs.uk",
"reason": "Patient transfer complete; clinical emergency resolved"
}
// Response (200 OK)
{
"status": "revoked",
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"revoked_at": "2026-09-20T00:35:10.000Z",
"original_expiration": "2026-09-20T00:50:00.000Z"
}Error Handling β Break-Glass Emergency Access
Standard failure codes for break-glass emergency elevation.
{
"error": "justification_too_short",
"message": "Clinical justification must be at least 20 characters long per tenant policy"
}{
"error": "duration_exceeds_maximum",
"message": "Requested duration (7200s) exceeds tenant maximum configured duration (3600s)"
}{
"error": "witness_required",
"message": "Dual-control clinical supervisor witness required for emergency elevation on this resource"
}Direct Policy Administration API
Direct schema and policy object administration for tenants. High-level REST CRUD operations that parse, update, and validate per-object-type definition blocks in the tenant's SpiceDB schema, with optimistic locking via ZetaTokens, namespace isolation, audit trail history, and schema export.
Base Path: /api/v1/tenant/policies Β |Β Auth: Bearer token (policy:manage or admin) Β |Β Engine: SpiceDB Schema AST Parser & Compiler
List & Retrieve Policy Definitions
List all object type definitions active in the tenant schema, or fetch a single definition block parsed into relations, permissions, comments, and the current consistency ZetaToken.
GET /api/v1/tenant/policies # List all parsed schema definitions
GET /api/v1/tenant/policies/{objectType} # Retrieve single definition block{
"items": [
{
"objectType": "patient_record",
"relations": ["owner", "clinician", "auditor"],
"permissions": ["view", "edit", "archive"],
"description": "Patient electronic health record governance policy",
"lastModified": "2026-09-19T21:40:00.000Z",
"tupleCount": 1420
},
{
"objectType": "clinical_trial_dossier",
"relations": ["investigator", "sponsor", "ethics_board"],
"permissions": ["read_protocol", "submit_evidence"],
"description": "Clinical research trial data access policy",
"lastModified": "2026-09-18T10:15:00.000Z",
"tupleCount": 380
}
],
"total": 2,
"zetaToken": "z_01HYX99ABC456DEF"
}{
"objectType": "patient_record",
"schemaText": "definition patient_record {\n relation owner: user\n relation clinician: user\n relation auditor: user\n permission view = owner + clinician + auditor\n permission edit = owner + clinician\n}",
"relations": ["owner", "clinician", "auditor"],
"permissions": ["view", "edit"],
"description": "Patient electronic health record governance policy",
"zetaToken": "z_01HYX99ABC456DEF",
"lastModified": "2026-09-19T21:40:00.000Z"
}Create, Update & Delete Policy Definitions
Append a new object type to the SpiceDB schema, update an existing definition block in-place with AST revalidation, or delete an obsolete policy definition.
POST /api/v1/tenant/policies # Append definition block to schema (201 Created)
PUT /api/v1/tenant/policies/{objectType} # Replace definition block in-place (200 OK)
DELETE /api/v1/tenant/policies/{objectType} # Delete definition block from schema (200 OK){
"objectType": "pathology_sample",
"schema": "definition pathology_sample {\n relation lab_technician: user\n relation pathologist: user\n permission verify = pathologist\n permission process = lab_technician + pathologist\n}"
}{
"objectType": "pathology_sample",
"created": true
}Version History & Schema Export
Inspect immutable schema revision history for regulatory compliance and export the entire tenant schema as a SpiceDB .zed file for local linting and testing.
GET /api/v1/tenant/policies/history?page=1&pageSize=10 # Paginated schema audit trail
GET /api/v1/tenant/policies/export # Download active schema as .zed file{
"items": [
{
"versionId": "ver_01HYX98827A",
"versionNumber": 4,
"author": "lead_architect@trust.nhs.uk",
"timestamp": "2026-09-19T21:40:00.000Z",
"changeReason": "Add pathology_sample definition for oncology diagnostics",
"zetaToken": "z_01HYX99ABC456DEF",
"additionsCount": 1,
"removalsCount": 0
},
{
"versionId": "ver_01HYX55219B",
"versionNumber": 3,
"author": "caldicott_officer@trust.nhs.uk",
"timestamp": "2026-09-15T14:20:00.000Z",
"changeReason": "Restricted auditor permissions on patient_record",
"zetaToken": "z_01HYX4477819AA",
"additionsCount": 0,
"removalsCount": 1
}
],
"total": 4,
"page": 1,
"pageSize": 10
}Error Handling β Policy Administration
Standard error responses for policy compiler and AST schema operations.
{
"error": "schema_compilation_failed",
"message": "parse error in definition 'patient_record' at line 3: relation 'supervisor' references undeclared type 'specialist'"
}{
"error": "not_found",
"message": "Object type 'radiology_scan' not found in tenant active schema"
}OIDC Discovery & Tenant Federation API
Standardized metadata documents and tenant federation discovery. Operates on an βhonest advertisementβ security disciplineβonly advertising endpoints actually served (token exchange, introspection, JWKS, AuthZEN, SSF, CIMD). Features dual-cache multi-tenant discovery (5m positive cache, 1m negative cache absorbing DoS and enumeration attacks) and RFC 9728 Protected Resource Metadata for MCP AI gateways.
Standards: RFC 8414 (OAuth Server Metadata) Β |Β RFC 7517 (JWKS) Β |Β RFC 9728 (Protected Resource Metadata) Β |Β OpenID Shared Signals (SSF)
OpenID Connect & OAuth Server Metadata
Retrieve platform or tenant-scoped discovery documents. Advertises RFC 8693 token exchange, RFC 9449 DPoP algorithms, Rich Authorization Requests (RAR), and AuthZEN runtime evaluation endpoints.
GET /.well-known/openid-configuration # Platform root OIDC discovery
GET /t/{tenant_id}/.well-known/openid-configuration # Multi-tenant scoped OIDC discovery
GET /.well-known/oauth-authorization-server # RFC 8414 OAuth authorization server metadata
GET /.well-known/oauth-protected-resource # RFC 9728 MCP tool gateway resource metadata
GET /.well-known/ssf-configuration # OpenID Shared Signals and Events (CAEP){
"issuer": "https://api.authhub.cloud",
"authorization_endpoint": "https://api.authhub.cloud/oauth/authorize",
"token_endpoint": "https://api.authhub.cloud/oauth/token",
"jwks_uri": "https://api.authhub.cloud/.well-known/jwks.json",
"response_types_supported": [
"code",
"token",
"id_token"
],
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"scopes_supported": [
"openid",
"profile",
"email",
"authhub:authorize",
"authhub:introspect"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post",
"private_key_jwt",
"none"
],
"token_endpoint_auth_signing_alg_values_supported": [
"ES256",
"RS256"
],
"grant_types_supported": [
"authorization_code",
"urn:ietf:params:oauth:grant-type:token-exchange",
"urn:ietf:params:oauth:grant-type:jwt-bearer"
],
"code_challenge_methods_supported": [
"S256"
],
"introspection_endpoint": "https://api.authhub.cloud/oauth/introspect",
"revocation_endpoint": "https://api.authhub.cloud/oauth/revoke",
"claims_supported": [
"sub",
"iss",
"aud",
"exp",
"iat",
"email",
"name",
"tenant_id"
],
"client_id_metadata_document_supported": true,
"authorization_grant_profiles_supported": [
"urn:ietf:params:oauth:grant-profile:id-jag"
],
"identity_chaining_requested_token_types_supported": [
"urn:ietf:params:oauth:token-type:id-jag"
],
"dpop_signing_alg_values_supported": [
"ES256",
"ES384",
"ES512",
"RS256"
],
"authzen_evaluation_endpoint": "https://api.authhub.cloud/authzen/v1/evaluation",
"ssf_configuration_endpoint": "https://api.authhub.cloud/.well-known/ssf-configuration"
}OAuth Protected Resource Metadata (MCP AI Tool Gateway)
Per RFC 9728 Β§3, protected resource servers (such as the AuthHub MCP Gateway) advertise their canonical resource identifier and authoritative authorization server list. Autonomous AI agents query this document after receiving an HTTP 401 challenge with a resource_metadata link.
GET /.well-known/oauth-protected-resource
GET /.well-known/oauth-protected-resource/mcp # RFC 9728 path-specific resource metadata{
"resource": "https://api.authhub.cloud/mcp",
"authorization_servers": [
"https://api.authhub.cloud"
],
"scopes_supported": [
"authhub:authorize"
],
"bearer_methods_supported": [
"header"
],
"resource_documentation": "https://api.authhub.cloud/docs/mcp-gateway"
}{
"issuer": "https://api.authhub.cloud",
"token_endpoint": "https://api.authhub.cloud/oauth/token",
"introspection_endpoint": "https://api.authhub.cloud/oauth/introspect",
"grant_types_supported": [
"authorization_code",
"urn:ietf:params:oauth:grant-type:token-exchange",
"urn:ietf:params:oauth:grant-type:jwt-bearer"
],
"subject_token_types_supported": [
"urn:ietf:params:oauth:token-type:jwt",
"urn:ietf:params:oauth:token-type:id_token",
"urn:ietf:params:oauth:token-type:access_token"
],
"requested_token_types_supported": [
"urn:ietf:params:oauth:token-type:access_token",
"urn:ietf:params:oauth:token-type:id-jag"
],
"dpop_signing_alg_values_supported": ["ES256", "ES384", "ES512", "RS256"],
"authorization_details_types_supported": ["resource_access", "fga_permission", "fga_batch", "scope_set"],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "private_key_jwt", "none"],
"scopes_supported": ["authhub:authorize", "authhub:introspect"],
"ssf_configuration_endpoint": "https://api.authhub.cloud/.well-known/ssf-configuration",
"service_documentation": "https://authhub.cloud/docs"
}Public JSON Web Key Set (JWKS)
Platform public keys used by resource servers and API gateways to verify RS256/ES256 signatures on access tokens, ID-JAG assertion bundles, and CAEP event streams. Enforces defense-in-depth stripping of all private key parameters.
GET /.well-known/jwks.json{
"keys": [
{
"kty": "RSA",
"use": "sig",
"alg": "RS256",
"kid": "authhub-sig-2026a",
"n": "u1P5xL...[base64url-modulus]...",
"e": "AQAB"
},
{
"kty": "EC",
"use": "sig",
"alg": "ES256",
"crv": "P-256",
"kid": "authhub-ec-2026b",
"x": "W8sZ...",
"y": "K9vP..."
}
]
}Multi-Region Tenant Routing Discovery
Provides edge gateways with regional partition routing information, ensuring requests are directed to the primary CockroachDB and SpiceDB clusters local to the NHS Trust.
GET /discovery/v1/tenants # List discoverable federated tenants
GET /discovery/v1/tenants/{tenant_id} # Inspect regional residency & gateway endpoints{
"tenant_id": "trust-042",
"display_name": "St Thomas Hospital NHS Trust",
"primary_region": "uksouth",
"replica_regions": ["ukwest"],
"api_gateway_endpoint": "https://uksouth.authhub.cloud",
"authzen_endpoint": "https://uksouth.authhub.cloud/authzen/v1/evaluation",
"spicedb_endpoint": "grpc://spicedb-uksouth.authhub.cloud:50051",
"data_residency_status": "in_region_enforced",
"active": true
}Hardware Security Module (HSM) & BYOK API
Cryptographic hardware isolation and Bring-Your-Own-Key (BYOK) lifecycle. Luna HSM (PKCS#11) integration with software fallback, AES-256-GCM envelope key wrapping ([12B nonce][16B auth tag][ciphertext]), zero-downtime key rotation with propagation delay windows, and tamper-evident partition status attestation.
Security Standard: FIPS 140-2 Level 3 Hardware Security Module Β |Β Algorithms: RSA-2048, AES-256-GCM, HMAC-SHA256 Β |Β Key Wrapping: RFC 3394 / RFC 5649 compliant
HSM Partition Status & Attestation
Inspect hardware partition health, PKCS#11 session pool utilization, firmware revision, cryptographic clock drift, and currently active signing keys.
GET /api/v1/tenant/hsm/status # Tenant HSM partition health & session stats
GET /api/operator/keys # Operator active key status & DPoP configuration{
"mode": "hsm",
"status": "ok",
"slotStatus": "OK",
"firmwareVersion": "Luna-7.8.1-FIPS",
"sessionPool": {
"active": 4,
"available": 28,
"total": 32
},
"clockDriftSeconds": 0.042,
"lastCheckAt": 1726790400000,
"hsm_enabled": true,
"active_key": {
"kid": "authhub-sig-2026a",
"algorithm": "RS256",
"keyType": "RSA",
"created_at": "2026-09-01T00:00:00.000Z"
}
}Trigger Zero-Downtime Key Rotation
Initiate zero-downtime key rotation. Generates a new hardware-bound RSA/EC keypair, publishes the new public key in the JWKS, and retains the prior key in dual-verification mode across a 24-hour propagation window to prevent verification errors on in-flight tokens.
POST /api/v1/operator/keys/rotate{
"operator_id": "secops-lead@trust.nhs.uk",
"reason": "Scheduled Q3 90-day cryptographic rotation",
"propagation_grace_seconds": 86400
}{
"status": "rotation_initiated",
"previous_kid": "authhub-sig-2026a",
"new_kid": "authhub-sig-2026b",
"algorithm": "RS256",
"active_at": "2026-09-20T00:30:00.000Z",
"grace_expires_at": "2026-09-21T00:30:00.000Z",
"retained_keys_count": 2
}Secure Envelope Key Wrapping (BYOK Import)
Enterprise tenants can import their own tenant-dedicated encryption keys. Keys must be wrapped using the platform Key Encryption Key (KEK) using AES-256-GCM wire format: [12-byte Nonce][16-byte Auth Tag][Ciphertext].
POST /api/v1/tenant/hsm/import{
"key_label": "trust-042-primary-dek",
"key_algorithm": "AES-256-GCM",
"wrapped_key_payload": "7a8b...[base64-encoded 12B nonce + 16B auth tag + ciphertext]...",
"kek_label": "platform-kek-v2",
"attestation_statement": "MIIB...[PKCS#11 hardware attestation token]..."
}{
"key_label": "trust-042-primary-dek",
"status": "imported_active",
"key_fingerprint": "sha256:d8a2...3f1c",
"imported_at": "2026-09-20T00:32:00.000Z"
}Client-ID-Metadata (CIMD) & Billing API
URL-based client admission policies (Client-ID-Metadata Documents per draft-ietf-oauth-client-id-metadata-document) and Stripe self-service subscription management. Enforces glob admission rules, plan limits, operator blocklists, and automated tier billing.
Plan Quotas: Developer (3 policies) Β |Β Starter (10 policies) Β |Β Professional (25 policies) Β |Β Enterprise (50 policies)
CIMD URL Ingress Admission Policies
Configure admission rules that whitelist or block client applications resolving via URL client IDs. Unmatched or non-compliant client URLs are rejected during token exchange.
POST /api/v1/tenant/cimd/policies/create # Create glob admission rule
GET /api/v1/tenant/cimd/policies/list # List configured rules and quota count
POST /api/v1/tenant/cimd/policies/update # Update pattern or enabled state
POST /api/v1/tenant/cimd/policies/delete # Remove admission rule{
"tenant_id": "trust-042",
"pattern": "https://*.clinical.trust.nhs.uk/client-metadata.json",
"mode": "allow",
"target_type": "client_id",
"description": "Permit verified hospital clinical applications"
}{
"policies": [
{
"id": "cpol_01HYX99KKL12",
"tenant_id": "trust-042",
"pattern": "https://*.clinical.trust.nhs.uk/client-metadata.json",
"mode": "allow",
"target_type": "client_id",
"description": "Permit verified hospital clinical applications",
"enabled": true,
"created_at": "2026-09-18T12:00:00.000Z"
}
],
"usage": {
"current": 1,
"max": 25,
"plan": "professional"
}
}Subscription & Stripe Checkout Operations
Self-service billing lifecycle. Inspect subscription status, launch Stripe Checkout sessions for upgrades, access the Stripe Customer Billing Portal, and download past invoices.
GET /api/v1/tenant/billing # Inspect tenant subscription & grace status
POST /api/v1/tenant/billing/checkout # Create Stripe Checkout Session (upgrade)
POST /api/v1/tenant/billing/portal # Create Stripe Customer Portal Session
GET /api/v1/tenant/billing/invoices # List past invoices and PDF download URLs
POST /api/v1/tenant/billing/downgrade # Schedule tier downgrade for end-of-period{
"tenant_id": "trust-042",
"plan": "professional",
"subscription_status": "active",
"current_period_end": "2026-10-15T00:00:00.000Z",
"payment_grace_until": null,
"downgrade_scheduled_plan": null,
"quotas": {
"max_sub_tenants": 50,
"max_monthly_checks": 5000000,
"max_cimd_policies": 25,
"dedicated_hsm": false
}
}// Request Body
{
"target_plan": "enterprise"
}
// Response (200 OK)
{
"checkout_url": "https://checkout.stripe.com/c/pay/cs_live_a1b2c3d4e5f6..."
}Platform Health, Metrics & Diagnostics API
Infrastructure health telemetry, liveness/readiness orchestration probes, Prometheus metrics exposition, and administrative session authentication with Redis-backed refresh token rotation and HttpOnly cookies.
Liveness & Readiness Health Probes
Deep cluster diagnostic probe verifying CockroachDB pool connections, Redis response latency, SpiceDB gRPC channel health, and Kafka broker connectivity. Returns HTTP 200 OK when fully healthy, or HTTP 503 Service Unavailable if any critical subsystem fails.
GET /health{
"status": "healthy",
"version": "0.1.0",
"uptime": 86420.5,
"timestamp": "2026-09-20T00:35:00.000Z",
"checks": {
"cockroachdb": {
"status": "ok",
"latency_ms": 1.2
},
"redis": {
"status": "ok",
"latency_ms": 0.4
},
"spicedb": {
"status": "ok",
"endpoint": "spicedb.internal:50051",
"latency_ms": 2.8
},
"kafka": {
"status": "ok",
"brokers_connected": 3
}
}
}Prometheus Telemetry Exposition
Prometheus exposition format for Grafana dashboards, Datadog agents, and autoscaling horizontal pod autoscalers (HPA).
GET /metrics# HELP http_requests_total Total number of HTTP requests processed
# TYPE http_requests_total counter
http_requests_total{method="POST",route="/authzen/v1/evaluation",status="200"} 481920
http_requests_total{method="POST",route="/api/v1/tenant/tuples",status="200"} 124010
http_requests_total{method="POST",route="/authzen/v1/evaluation",status="403"} 1290
# HELP http_request_duration_seconds Latency histogram for HTTP requests
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.005",route="/authzen/v1/evaluation"} 421000
http_request_duration_seconds_bucket{le="0.010",route="/authzen/v1/evaluation"} 479000
http_request_duration_seconds_bucket{le="+Inf",route="/authzen/v1/evaluation"} 481920
# HELP spicedb_lookup_duration_seconds Latency of SpiceDB gRPC relationship evaluations
# TYPE spicedb_lookup_duration_seconds histogram
spicedb_lookup_duration_seconds_sum 1248.42
spicedb_lookup_duration_seconds_count 605930
# HELP active_requests Number of requests currently executing
# TYPE active_requests gauge
active_requests 14Administrative Credential Authentication & Sessions
User authentication for tenant administrators and operators. Issues dual HttpOnly, Secure cookies (auth_access and auth_refresh) with automated token rotation and Redis-backed refresh token family invalidation.
POST /auth/login # Authenticate credentials & issue session cookies
POST /auth/refresh # Cookie-based refresh token rotation
POST /auth/logout # Revoke refresh token family in Redis and clear cookies{
"email": "admin@trust.nhs.uk",
"password": "CorrectHorseBatteryStaple!2026"
}// Headers: Set-Cookie: auth_access=...; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=900
// Set-Cookie: auth_refresh=...; HttpOnly; Secure; SameSite=Lax; Path=/api/auth/refresh; Max-Age=604800
{
"user": {
"id": "usr_01HYX12345ABC",
"email": "admin@trust.nhs.uk",
"role": "tenant_admin",
"tenant_id": "trust-042",
"name": "Sarah Connor",
"email_verified": true
},
"expires_in": 900
} Continuous Regression Suite & Synthetic Probes
AuthHub operates an automated continuous test harness verifying all 37 critical platform subsystems across five isolation tiers: ReBAC Core, AuthZEN SARC, SCIM 2.0 lifecycle, WIF & Token Exchange, MCP Edge Gateway, Break-Glass emergency overrides, Luna Cloud HSM/BYOK, and zero-drift OpenAPI contracts.
GET /api/tenant/testing # Execute full 37-component regression test suite
GET /api/tenant/testing/mcp-gateway # Live MCP Gateway protocol & RFC 9728 synthetic probe
POST /api/tenant/testing/mcp-gateway # Trigger on-demand MCP protocol synthetic diagnosticsLive MCP Gateway Synthetic Probe (5-Step Diagnostic)
Executes five live HTTP probes against the deployed gateway to verify standards compliance, version negotiation, header security, and token exchange readiness:
- Probe 1: RFC 9728 Protected Resource Metadata Discovery β Verifies
GET /.well-known/oauth-protected-resourcereturns canonical resourcehttps://api.authhub.cloud/mcpand authorized AS. - Probe 2: MCP Root & Gateway Protocol Negotiation β Probes
POST /mcpwith statelessserver/discover(MCP 2026-07-28 spec). - Probe 3: RFC 9728 401 Challenge Header Validation β Validates unauthenticated requests receive HTTP 401 with well-formed
WWW-Authenticatepointing to the resource metadata URL. - Probe 4: OpenID Connect Discovery 1.0 Compliance β Checks
GET /.well-known/openid-configurationsatisfies strict deserializers with all standard endpoints and ID-JAG grant profiles. - Probe 5: Token Exchange & ID-JAG Readiness β Validates
POST /oauth/tokenhandlesclient_secret_basicauthentication and ID-JAG token exchange parameters.
{
"summary": {
"total": 5,
"passed": 5,
"failed": 0,
"duration_ms": 42
},
"results": [
{ "probe": "rfc9728_protected_resource", "status": "pass", "http_status": 200, "duration_ms": 6 },
{ "probe": "mcp_server_discover", "status": "pass", "http_status": 200, "duration_ms": 8 },
{ "probe": "rfc9728_401_challenge", "status": "pass", "http_status": 401, "duration_ms": 5 },
{ "probe": "oidc_discovery_compliance", "status": "pass", "http_status": 200, "duration_ms": 11 },
{ "probe": "token_exchange_readiness", "status": "pass", "http_status": 200, "duration_ms": 12 }
]
}Database Access Gateways & Connections API
Zero-Filesystem OnboardingProvides enterprise customers with a pure REST API control plane to onboard cloud databases (Aiven, RDS, Cloud SQL) without requiring filesystem access or cluster shell credentials. Automatically provisions isolated SpiceDB-governed query gateway URLs, enforces AST query validation, evaluates AuthZEN SARC policies, performs deterministic HMAC PII masking, and equips human owners with sub-millisecond Clock-1 emergency kill switches.
Path Support: AuthHub supports both standard REST paths (/api/v1/tenant/database-connections) and direct root aliases (/v1/tenant/database-connections) interchangeably.
Register Cloud Database Connection
Registers an external database (PostgreSQL, CockroachDB, MySQL). AuthHub performs an immediate live TCP/TLS handshake and schema introspection, registers SpiceDB ReBAC governance tuples with verified SCIM directory owners (e.g. from Entra ID connection n9), and assigns an isolated gateway proxy URL.
POST /api/v1/tenant/database-connections
POST /v1/tenant/database-connectionsAuthorization: Bearer <tenant_admin_token>
x-tenant-id: <tenant_id>
Content-Type: application/json{
"name": "aiven-prod-postgresql",
"database_type": "postgres",
"connection_string": "postgres://avnadmin:SECRET@pg-authhub1-authhub-poc1.f.aivencloud.com:10952/defaultdb?sslmode=require",
"ssl_mode": "require",
"governance": {
"technical_owner": "Hanif@NiloDevelopments.onmicrosoft.com",
"business_owner": "aaron.barlow@authhub.cloud",
"deputies": [
"abby.nguyen@authhub.cloud",
"adam.atkins@authhub.cloud"
],
"escalation_contact": "Hanif@NiloDevelopments.onmicrosoft.com",
"scim_connection_id": "n9",
"pii_masking_enabled": true,
"max_query_execution_time_ms": 5000,
"allowed_tables": [
"patients",
"clinical_records",
"appointments",
"departments"
]
}
}{
"status": "success",
"connection": {
"id": "8f700688-46fb-40a2-a05e-a6119f6f6004",
"tenant_id": "tenant-poc-01",
"name": "aiven-prod-postgresql",
"database_type": "postgres",
"assigned_gateway_url": "https://api.authhub.cloud/api/v1/tenant/database-connections/8f700688-46fb-40a2-a05e-a6119f6f6004/query",
"status": "active",
"kill_switch_active": false,
"introspected_tables": [
"patients",
"clinical_records",
"appointments",
"departments"
],
"created_at": "2026-09-21T12:00:00.000Z"
}
}List Registered Database Connections
Returns all registered database connections for the authenticated tenant, including status, introspected table schemas, and assigned gateway URLs.
GET /api/v1/tenant/database-connections
GET /v1/tenant/database-connections{
"status": "success",
"connections": [
{
"id": "8f700688-46fb-40a2-a05e-a6119f6f6004",
"name": "aiven-prod-postgresql",
"database_type": "postgres",
"status": "active",
"kill_switch_active": false,
"assigned_gateway_url": "https://api.authhub.cloud/api/v1/tenant/database-connections/8f700688-46fb-40a2-a05e-a6119f6f6004/query",
"created_at": "2026-09-21T12:00:00.000Z"
}
]
}Execute Gated Database Query
Executes Text-to-SQL or parameterized queries on behalf of AI agents or workload services. The request is subject to AuthZEN SARC authorization, AST safety checks (blocking unauthorized mutations, DROP/TRUNCATE, or cross-tenant joins), SpiceDB ReBAC table access evaluation, and dynamic HMAC PII masking.
POST /api/v1/tenant/database-connections/{id}/query
POST /v1/tenant/database-connections/{id}/query{
"sql_query": "SELECT patient_id, full_name, national_id, primary_condition, assigned_ward FROM patients WHERE department_id = 'dept-cardiology' LIMIT 5;",
"agent_context": {
"agent_id": "agent-clinical-copilot",
"purpose": "clinical_summary",
"justification": "Consultation review for Ward 4B",
"client_session_id": "sess-9941"
}
}{
"status": "success",
"execution_time_ms": 14.2,
"row_count": 1,
"masked_columns": [
"national_id"
],
"governance_audit": {
"authzen_decision": "PERMIT",
"spicedb_checked": true,
"sarc_evaluated": true,
"ast_sanitized": true
},
"rows": [
{
"patient_id": "pat-001",
"full_name": "Eleanor Vance",
"national_id": "HMAC-SHA256:8f4a13c9e...",
"primary_condition": "Cardiomyopathy",
"assigned_ward": "Ward 4B"
}
]
}Emergency Kill Switch (Clock-1 Safety Substrate)
Immediately terminates and severs all agent queries and database gateway connections in sub-millisecond execution time. Authorized human technical owners, business owners, or deputies can suspend or restore access instantly during suspected anomalies or red-team alerts.
POST /api/v1/tenant/database-connections/{id}/kill-switch
POST /v1/tenant/database-connections/{id}/kill-switch{
"action": "suspend",
"reason": "Anomalous multi-record exfiltration attempt detected by SIEM",
"authorized_by": "Hanif@NiloDevelopments.onmicrosoft.com"
}{
"status": "success",
"connection_id": "8f700688-46fb-40a2-a05e-a6119f6f6004",
"kill_switch_active": true,
"updated_at": "2026-09-21T12:05:00.000Z",
"message": "Gateway successfully isolated. All inbound queries will be rejected with 403 Forbidden until restored."
}Integration Guide
Add authorization to your Node.js application (Express, Fastify, Next.js, NestJS) in under 10 minutes. No protobuf tooling required β AuthHub accepts standard HTTP/JSON via the Connect protocol.
1. Install
Install the official SDK from npm, or use any HTTP client (fetch, axios) for a lightweight integration.
npm install @auth-hub/sdk# Nothing to install β use native fetch (Node 18+)2. Configure Client
Create a reusable AuthHub client. In development, use Bearer token auth. In production, upgrade to mTLS for zero-trust.
// lib/authhub.ts β Works with any Node.js framework
const AUTHHUB_URL = process.env.AUTHHUB_URL || 'https://api.authhub.cloud';
const AUTHHUB_TOKEN = process.env.AUTHHUB_TOKEN!;
const TENANT_ID = process.env.AUTHHUB_TENANT_ID!;
const SUB_TENANT_ID = process.env.AUTHHUB_SUB_TENANT_ID!;
async function authhub(endpoint: string, body: object) {
const res = await fetch(`${AUTHHUB_URL}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${AUTHHUB_TOKEN}`,
'X-Tenant-ID': TENANT_ID,
'X-Sub-Tenant-ID': SUB_TENANT_ID,
},
body: JSON.stringify(body),
});
if (!res.ok) throw new Error(`AuthHub ${res.status}: ${await res.text()}`);
return res.json();
}
// βββ Permission Check βββββββββββββββββββββββββββββββββββββββββββ
export async function checkPermission(
userId: string,
permission: string,
resourceType: string,
resourceId: string,
): Promise<boolean> {
const data = await authhub('/nhs.rebac.v1.PermissionCheckService/CheckPermission', {
subject: { object: { objectType: 'user', objectId: userId } },
permission,
resource: { objectType: resourceType, objectId: resourceId },
consistency: { fullyConsistent: true },
});
return data.permissionship === 'PERMISSIONSHIP_HAS_PERMISSION';
}
// βββ Write Tuple ββββββββββββββββββββββββββββββββββββββββββββββββ
export async function writeTuple(
subjectType: string, subjectId: string,
relation: string,
resourceType: string, resourceId: string,
) {
return authhub('/nhs.rebac.v1.TupleService/WriteTuple', {
operation: 'OPERATION_CREATE',
tuple: {
subject: { object: { objectType: subjectType, objectId: subjectId } },
relation,
resource: { objectType: resourceType, objectId: resourceId },
},
});
}
// βββ Delete Tuple βββββββββββββββββββββββββββββββββββββββββββββββ
export async function deleteTuple(
subjectType: string, subjectId: string,
relation: string,
resourceType: string, resourceId: string,
) {
return authhub('/nhs.rebac.v1.TupleService/WriteTuple', {
operation: 'OPERATION_DELETE',
tuple: {
subject: { object: { objectType: subjectType, objectId: subjectId } },
relation,
resource: { objectType: resourceType, objectId: resourceId },
},
});
}# Development (Bearer token auth)
AUTHHUB_URL=https://api.authhub.cloud
AUTHHUB_TOKEN=your-api-credential-token
AUTHHUB_TENANT_ID=your-tenant-uuid
AUTHHUB_SUB_TENANT_ID=your-sub-tenant-uuid3. Express / Fastify Middleware
Drop-in middleware that gates any route behind an AuthHub permission check. Works with Express, Fastify, or any framework that uses req/res/next.
import { checkPermission } from '../lib/authhub';
import { Request, Response, NextFunction } from 'express';
/**
* Reusable authorization middleware.
* Usage: app.get('/documents/:id', authorize('view', 'document'), handler)
*/
export function authorize(permission: string, resourceType: string) {
return async (req: Request, res: Response, next: NextFunction) => {
const userId = req.user?.id; // From your auth middleware (Passport, JWT, etc.)
const resourceId = req.params.id;
if (!userId || !resourceId) {
return res.status(400).json({ error: 'Missing user or resource ID' });
}
const allowed = await checkPermission(userId, permission, resourceType, resourceId);
if (!allowed) {
return res.status(403).json({
error: 'forbidden',
message: `User ${userId} does not have '${permission}' on ${resourceType}:${resourceId}`,
});
}
next();
};
}
// βββ Route examples βββββββββββββββββββββββββββββββββββββββββββββ
app.get('/api/documents/:id', authorize('view_document', 'document'), getDocument);
app.put('/api/documents/:id', authorize('edit_document', 'document'), updateDocument);
app.delete('/api/documents/:id', authorize('delete_document', 'document'), deleteDocument);
app.get('/api/patients/:id/record', authorize('view_record', 'patient'), getPatientRecord);4. Provisioning Relationships
When users create resources, get assigned roles, or join teams β push the relationship to AuthHub. This is the βwrite pathβ that powers the permission checks above.
import { writeTuple, deleteTuple } from '../lib/authhub';
// When a user creates a document β they become the owner
async function onDocumentCreated(userId: string, docId: string) {
await writeTuple('user', userId, 'owner', 'document', docId);
}
// When a user is added to a team β they get team membership
async function onUserJoinsTeam(userId: string, teamId: string) {
await writeTuple('user', userId, 'member', 'team', teamId);
}
// When a user leaves a team β remove the relationship
async function onUserLeavesTeam(userId: string, teamId: string) {
await deleteTuple('user', userId, 'member', 'team', teamId);
}
// When a clinician is assigned to a patient
async function onClinicalAssignment(clinicianId: string, patientId: string) {
await writeTuple('user', clinicianId, 'treating_clinician', 'patient', patientId);
}5. AuthZEN Policy Evaluation (Optional)
For standard-compliant integrations (OpenID AuthZEN 1.0), use the evaluation endpoint. This is ideal for organisations requiring portable, vendor-neutral authorization decisions.
// AuthZEN-compliant evaluation (OpenID standard)
async function evaluateAccess(subject: string, action: string, resource: string) {
const res = await fetch(`${AUTHHUB_URL}/authzen/v1/evaluation`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${AUTHHUB_TOKEN}`,
'X-Tenant-ID': TENANT_ID,
},
body: JSON.stringify({
subject: { type: 'user', id: subject },
action: { name: action },
resource: { type: 'patient', id: resource },
}),
});
const data = await res.json();
return data.decision === true;
}6. CI/CD Integration (Workload Identity)
For GitHub Actions, Kubernetes, or cloud functions β use Workload Identity Federation instead of storing API keys. See the complete workflow in the API Reference β WIF section.
- name: Get AuthHub token (zero secrets)
run: |
OIDC_TOKEN=$(curl -sS -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=authhub.nhs.uk" | jq -r '.value')
AUTHHUB_TOKEN=$(curl -sS -X POST https://api.authhub.cloud/oauth/token \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=$OIDC_TOKEN" \
-d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \
-d "scope=schema:write" | jq -r '.access_token')7. Production: Upgrade to mTLS
For zero-trust production environments, replace Bearer token auth with mutual TLS. Download your certificate from the Console β Certificates page.
import { createClient } from '@connectrpc/connect';
import { createGrpcTransport } from '@connectrpc/connect-node';
import * as fs from 'node:fs';
const transport = createGrpcTransport({
baseUrl: 'https://api.authhub.cloud',
httpVersion: '2',
nodeOptions: {
cert: fs.readFileSync('./certs/client.crt'),
key: fs.readFileSync('./certs/client.key'),
ca: fs.readFileSync('./certs/ca.crt'),
},
interceptors: [(next) => async (req) => {
req.header.set('X-Tenant-ID', process.env.AUTHHUB_TENANT_ID!);
req.header.set('X-Sub-Tenant-ID', process.env.AUTHHUB_SUB_TENANT_ID!);
return next(req);
}],
});β οΈ Consistency Trade-offs
Use fullyConsistent: true for security-critical checks (data access, financial operations, clinical decisions). Use minimizeLatency: true for non-critical UI hints (hiding buttons, rendering menus) where ~1-2s eventual consistency is acceptable.
π¦ @auth-hub/sdk β Available on npm
The official SDK includes TypeScript types, auto-retry, and pre-built service clients. Install with npm install @auth-hub/sdk. The HTTP/JSON approach above works as a lightweight alternative for any Node 18+ runtime.
Schema Patterns
Authorization patterns for any B2B application β from SaaS startups to regulated enterprises. Each pattern includes the SpiceDB schema AND the HTTP request to create the relationship.
Vertical Starter Templates & Sector Configuration
AuthHub provides pre-tested, out-of-the-box Zanzibar ReBAC schemas tailored to specific regulatory and organizational domains. Tenants select their sector (industry: "healthcare" | "enterprise") during registration, and AuthHub automatically binds the appropriate starter schema:
Compliant with NHS Caldicott principles and IG regulations. Includes emergency break-glass overrides, clinical care teams, episodes of care, and Caldicott Guardian review relations.
Objects: patient, organization, team, episode_of_care, clinical_record
Multi-tenant B2B SaaS architecture with organizational tenancy, workspace scoping, project hierarchies, inherited document access, and machine service accounts.
Objects: tenant, organization, workspace, project, document, service_account
Query the template catalog via GET /api/v1/tenant/schema/templates or select templates dynamically in the Console Schema Editor.
Core SaaS Patterns
Standard B2B use cases that form the foundation of any multi-tenant application.
1. Multi-Tenant B2B SaaS
The foundational pattern: users belong to organisations, organisations own workspaces, workspaces contain documents. Permissions cascade through the hierarchy.
definition user {}
definition organization {
relation admin: user
relation member: user
permission manage = admin
permission access = admin + member
}
definition workspace {
relation parent_org: organization
relation owner: user
relation viewer: user
// Org admins inherit full access
permission manage = owner + parent_org->admin
permission view = viewer + owner + parent_org->member
}
definition document {
relation parent_workspace: workspace
relation author: user
// Inherited through workspace membership
permission edit = author + parent_workspace->manage
permission view = author + parent_workspace->view
}POST /v1/relationships
{
"resource": { "type": "organization", "id": "acme-corp" },
"relation": "member",
"subject": { "type": "user", "id": "user_8f3k2j" }
}2. Team & Project Hierarchy
Teams own projects, projects contain tasks. Team leads inherit management permissions; contributors can view and update assigned tasks.
definition user {}
definition team {
relation lead: user
relation member: user
permission manage = lead
permission participate = lead + member
}
definition project {
relation owning_team: team
relation contributor: user
permission manage = owning_team->manage
permission view = contributor + owning_team->participate
}
definition task {
relation parent_project: project
relation assignee: user
permission update = assignee + parent_project->manage
permission view = assignee + parent_project->view
}POST /v1/relationships
{
"resource": { "type": "team", "id": "platform-eng" },
"relation": "member",
"subject": { "type": "user", "id": "user_4a9c1b" }
}3. Break-Glass / Emergency Access
For customer support impersonation, emergency DevOps access, or clinical emergencies. All break-glass access is time-limited and SOC2-auditable.
definition user {}
definition organization {
relation member: user
relation admin: user
relation emergency_responder: user
}
definition resource {
relation owner: user
relation org: organization
// Normal access through ownership
permission view = owner
// Break-glass: any org member with emergency_responder role
permission emergency_access = org->emergency_responder
// Combined: normal access OR emergency override
permission access = view + emergency_access
}π¨ Break-Glass Auditing
All break-glass access is automatically logged with reason codes, reviewer assignments, and expiry timestamps. Compliant with SOC2, ISO 27001, and healthcare audit requirements.
Automated Enterprise Onboarding (SCIM β ReBAC)
Allow enterprise clients to manage your app's permissions from their own Entra ID or Okta. When they remove a user in Okta, AuthHub instantly revokes access in your app.
4. Group-to-Relation Mapping
Map SCIM groups from your IdP directly to SpiceDB relationships. Group membership drives authorization without manual tuple management.
// Groups synced from Entra ID / Okta via SCIM
definition user {}
definition group {
relation member: user
}
definition workspace {
relation editor_group: group
relation viewer_group: group
// Access inherited through IdP group membership
permission edit = editor_group->member
permission view = viewer_group->member + editor_group->member
}
// When SCIM provisions a user into "Engineering-Editors" group:
// AuthHub auto-creates: group:engineering-editors#member@user:jane
// When admin assigns group to workspace:
// workspace:main#editor_group@group:engineering-editors
// Result: jane can edit workspace:main (inherited through group)POST /v1/relationships
{
"resource": { "type": "group", "id": "engineering-editors" },
"relation": "member",
"subject": { "type": "user", "id": "user_jane_okta" }
}Lifecycle sync: When a user is removed from an IdP group, SCIM PATCH removes the member relationship. The user immediately loses all permissions inherited through that group β no manual cleanup needed.
5. Department Hierarchy
Map the urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department attribute to departmental access boundaries. Heads of parent departments inherit visibility into sub-departments.
definition user {}
definition department {
relation head: user
relation member: user
relation parent: department
// Heads of parent departments can view all sub-departments
permission view_all = head + parent->view_all
permission manage = head
}
definition document {
relation owning_dept: department
relation author: user
// Department members can view; heads can approve
permission view = author + owning_dept->member
permission approve = owning_dept->head
}
// SCIM sync: when user's department changes (org transfer),
// AuthHub removes old membership tuple, adds new one.
// Access changes are immediate and audited.POST /v1/relationships
{
"resource": { "type": "department", "id": "engineering" },
"relation": "head",
"subject": { "type": "user", "id": "user_vp_eng" }
}Machine Identity (RFC 8693 Token Exchange)
Eliminate static API keys. CI/CD pipelines and Kubernetes pods authenticate using platform-native OIDC tokens.
6. Workload Scopes
Model machine-to-machine permissions using workload identities. Workloads are granted specific scopes through the FGA graph β same model as human users, ensuring consistent policy enforcement.
definition user {}
definition workload {
relation tenant: tenant
relation assigned_scope: scope
permission authenticate = tenant
}
definition scope {
relation assignee: user | workload
permission use = assignee
}
definition tenant {
relation admin: user
}
// Grant a CI/CD workload the schema:write scope:
// scope:schema_write#assignee@workload:wkld_7f3a2b1c4d5e
//
// On token exchange, AuthHub checks:
// BulkCheckPermission(workload:wkld_7f3a, use, scope:schema_write) β PERMITTED
//
// The issued token carries: scope="schema:write"
// The workload can now call WriteSchema7. CI/CD Pipeline (Environment-Scoped)
Different deployment environments (dev, staging, prod) get different permission sets β enforced through claim filters and scope restrictions.
{
"issuer_url": "https://token.actions.githubusercontent.com",
"claim_filters": [
{ "claim": "repository_owner", "operator": "equals", "value": "your-org" },
{ "claim": "environment", "operator": "in", "value": ["production", "staging"] }
],
"scope_restrictions": ["schema:*", "tuple:write", "audit:read"],
"claim_mappings": [
{ "mapping_type": "template", "target_attribute": "identity", "template": "${repository}:${environment}" }
]
}
// Result:
// - Only your-org repos in production/staging can exchange tokens
// - Tokens are limited to schema/tuple/audit operations
// - Each repo+environment combination gets a unique workload identity
// - Dev branches are blocked (no environment claim = filter fails)8. Kubernetes Service Mesh
Kubernetes pods authenticate using projected ServiceAccount tokens. Namespace-based filtering ensures only production workloads in approved namespaces can access AuthHub.
{
"issuer_url": "https://oidc.eks.eu-west-2.amazonaws.com/id/ABC123",
"claim_filters": [
{ "claim": "kubernetes.io.namespace", "operator": "in", "value": ["production", "staging"] }
],
"claim_mappings": [
{ "mapping_type": "direct", "source_claim": "sub", "target_attribute": "k8s_identity" }
],
"auto_provision_enabled": true,
"auto_provision_daily_limit": 20,
"max_token_lifetime_seconds": 300
}
// K8s sub claim format: "system:serviceaccount:production:schema-migrator"
// β Deterministic UUIDv5: always the same for this ServiceAccount
// β 5-minute tokens (pods re-exchange frequently)
// β Only production/staging namespaces acceptedIndustry: Healthcare (NHS)
These patterns demonstrate AuthHub's capabilities for regulated healthcare environments. The same architecture works for any compliance-heavy vertical (finance, government, legal).
9. PatientβPractitioner Access
Model clinical relationships between healthcare professionals and patients. Permissions are granted through direct care relationships.
definition user {}
definition patient {
relation treating_clinician: user
relation gp: user
relation ward_nurse: user
relation specialist: user
// Direct access through care relationships
permission view_record = treating_clinician + gp + ward_nurse + specialist
permission edit_record = treating_clinician + gp
permission prescribe = treating_clinician + gp
permission view_sensitive = treating_clinician
}POST /v1/relationships
{
"resource": { "type": "patient", "id": "patient_nhs_123" },
"relation": "treating_clinician",
"subject": { "type": "user", "id": "user_dr_smith" }
}10. WardβStaff Hierarchy
Model ward-based access where staff inherit permissions through ward membership, and managers have additional administrative capabilities.
definition user {}
definition ward {
relation manager: user
relation senior_nurse: user
relation staff: user
// Hierarchical permissions
permission view_patients = manager + senior_nurse + staff
permission manage_staff = manager
permission approve_discharge = manager + senior_nurse
permission view_controlled_drugs = manager + senior_nurse
}
definition patient {
relation admitted_to: ward
// Inherit ward-level access
permission view_record = admitted_to->staff
permission discharge = admitted_to->approve_discharge
}POST /v1/relationships
{
"resource": { "type": "patient", "id": "patient_nhs_456" },
"relation": "admitted_to",
"subject": { "type": "ward", "id": "ward_a3_cardiology" }
}11. Multi-Organisation Sharing
Model data sharing between healthcare organisations (e.g., between a GP practice and an acute trust) using organisational relationships and sharing agreements.
definition user {}
definition organisation {
relation member: user
relation admin: user
}
definition sharing_agreement {
relation provider_org: organisation
relation consumer_org: organisation
relation scope: permission_scope
permission can_share = provider_org->admin
permission can_receive = consumer_org->member
}
definition permission_scope {}
definition patient {
relation registered_at: organisation
relation shared_via: sharing_agreement
// Access through registration or sharing agreement
permission view_record = registered_at->member + shared_via->can_receive
}12. AuthZEN Agent Evaluation
Model AI agent access alongside human practitioners. Agents inherit permissions from their registering organisation but are constrained by agent-specific policies (scope, purpose limitation, data minimisation).
definition user {}
definition organisation {
relation member: user
relation admin: user
}
definition agent {
relation registered_by: organisation
relation approved_for: permission_scope
// Agent can only act within approved scopes
permission evaluate = registered_by->member & approved_for
}
definition permission_scope {}
definition patient {
relation treating_clinician: user
relation org: organisation
// Humans: direct care relationship
permission view_record = treating_clinician
// Agents: must be from same org AND approved for patient_data scope
permission agent_view_record = org->member
}AuthZEN context.agent: When an AI agent makes an evaluation request, AuthHub validates both the agent's registration status AND the human subject's access β preventing agents from accessing data their operator couldn't access directly.
13. COAZ FHIR Mapping
Translate incoming FHIR or HL7 healthcare payloads directly into AuthZEN SARC evaluations without writing intermediary middleware.
{
"version": "1.0",
"source_system": "epic_fhir_r4",
"mappings": {
"subject": {
"type": { "default": "practitioner" },
"id": { "source": "jwt_sub" }
},
"action": {
"name": {
"path": "$.intent",
"transform": "fhir_intent"
}
},
"resource": {
"type": { "path": "$.resourceType", "transform": "lowercase" },
"id": { "path": "$.id" }
}
},
"transforms": {
"fhir_intent": {
"read": "view_record",
"write": "edit_record",
"create": "create_record"
}
}
}Contextual Authorization (The "Circuit Breaker")
In enterprise SaaS, organizational structures change constantly. When a user changes departments or a company undergoes a merger, standard authorization systems often leave "stale" permissions active β leading to data leaks and failed SOC2 audits. AuthHub's Governance Engine suspends access instantly via an O(1) circuit breaker until an administrator provides explicit re-approval.
Available on Professional and Enterprise tiers
Contextual authorization requires governance policies to be configured via the tenant console or API. Solves SOC2 CC6.1 (access revocation timeliness) and ISO 27001 A.9.2.6 (removal of access rights).
The Three-Clock Governance Model
To prevent authorizations from operating under "old paperwork," AuthHub evaluates three temporal checkpoints:
β° Clock 1: Event Time β "Has the user's context changed?"
Fires when a structural identity event occurs. When a SCIM PATCH updates a monitored field (like department, manager, or costCenter), access is suspended instantly. Because this uses an O(1) Redis kill-switch rather than mutating thousands of underlying graph tuples, there is zero write amplification and zero delay.
SaaS example: An employee moves from Engineering to Sales in Okta. AuthHub instantly suspends their access to production AWS keys and source code repositories in your app.
βοΈ Clock 2: Attestation Time β "Who approved the renewal?"
Designated approvers (e.g., Department Heads, IT Security Admins) must provide explicit justification confirming the user still requires access under their new organizational context.
SaaS example: The IT Admin reviews the suspension and confirms: "Yes, this user still needs billing access in their new Sales role for the Q4 reporting cycle."
βοΈ Clock 3: Execution Time β "Is it safe to proceed right now?"
Evaluated on every single permission check. AuthHub verifies: (1) no active suspension, (2) attestation not stale (inline, zero-window), (3) no environmental drift since attestation. If any condition fails, the request is denied before the core permission engine is even queried.
SaaS example: When the user clicks "Export Customer Data," AuthHub checks the admissibility boundary. If the admin hasn't approved the department change yet, access is denied instantly β no data leaks.
How It Works
- 1Configure policies β Define which SCIM attribute changes trigger governance (organisation, manager, cost centre). Set required attestations, approver roles, and max-age for re-attestation.
- 2Write tuples with governance caveat β Attach the
governance_gatecaveat to relationships that require oversight. - 3Structural change fires β When a SCIM update matches a policy trigger, access is suspended instantly (O(1) Redis write, no tuple mutations).
- 4Approver re-attests β Designated approvers receive notification and must provide justification + environmental condition declarations via the console.
- 5Access restored or revoked β On approval, access is restored within seconds. On rejection or auto-expiry, structural tuples are deleted asynchronously.
API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/tenant/governance/policies | Create a governance policy |
| GET | /api/v1/tenant/governance/policies | List policies |
| PUT | /api/v1/tenant/governance/policies/:id | Update a policy |
| GET | /api/v1/tenant/governance/suspensions | List suspensions (filterable) |
| POST | /api/v1/tenant/governance/suspensions/:id/attest | Approve a suspension |
| POST | /api/v1/tenant/governance/suspensions/:id/reject | Reject (permanently revoke) |
| POST | /api/v1/tenant/governance/drift-declarations | Declare environmental drift |
| GET | /api/v1/tenant/governance/audit | Governance audit trail |
Detailed API Reference
Create Policy
Create a governance policy that defines when structural changes trigger access suspension.
POST /api/v1/tenant/governance/policies{
"name": "Department Change Governance",
"triggerFields": [
{ "path": "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department" },
{ "path": "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:costCenter" }
],
"affectedRelations": ["viewer", "editor", "billing_access"],
"requiredAttestations": [
{ "type": "manager_approval", "approverRole": "department_head" },
{ "type": "security_review", "approverRole": "it_sec_admin" }
],
"autoExpireDays": 14,
"attestationMaxAgeDays": 90,
"redisFailureMode": "fail_closed",
"priority": 10
}{
"id": "a1b2c3d4-...",
"name": "Department Change Governance",
"triggerFields": [{ "path": "...department" }, { "path": "...costCenter" }],
"affectedRelations": ["viewer", "editor", "billing_access"],
"requiredAttestations": [...],
"autoExpireDays": 14,
"attestationMaxAgeDays": 90,
"redisFailureMode": "fail_closed",
"priority": 10,
"version": 1,
"enabled": true,
"createdAt": "2026-07-03T12:00:00.000Z"
}triggerFieldsβ SCIM attribute paths. Valid: organization, manager, costCenter, department.affectedRelationsβ SpiceDB relations to govern. Use["*"]for all (with caution).attestationMaxAgeDaysβ Clock 3: force periodic re-attestation. Null = no expiry.redisFailureModeβfail_open(skip check if Redis down) orfail_closed(deny if Redis down).priorityβ Higher value wins when multiple policies conflict.
Attest Suspension (Approve)
Provide dual-gate attestation to restore access. Requires all attestation types + capability declaration.
POST /api/v1/tenant/governance/suspensions/:id/attest{
"attestations": [
{
"type": "manager_approval",
"justification": "Confirmed user still requires billing access in new Sales role for Q4 reporting cycle"
},
{
"type": "security_review",
"justification": "Security review confirms no elevated risk from department transfer. SOC2 access review completed."
}
],
"conditionsAtAttestation": [
{ "type": "department", "value": "Sales" },
{ "type": "compliance_framework", "value": "soc2_type2" }
],
"capabilityDeclaration": [
{
"category": "data_handling",
"assertion": "User has completed annual security awareness training",
"verifiedAt": "2026-07-01T00:00:00.000Z"
}
]
}{ "message": "Suspension attested successfully" }- All required attestation types (from the policy) must be provided.
- Justification must be β₯20 characters per attestation.
conditionsAtAttestationβ Environmental conditions at approval time (used for drift detection).capabilityDeclarationβ Asserts the user/org is capable and compliant.- Access is restored within seconds (Redis key deleted + Pub/Sub L1 invalidation).
Declare Environmental Drift
Report an environmental change that doesn't flow through SCIM. Automatically re-suspends subjects whose attestations conflict.
POST /api/v1/tenant/governance/drift-declarations{
"conditionType": "vendor_contract",
"oldValue": "VendorX (active)",
"newValue": "VendorX (terminated)",
"effectiveDate": "2026-07-01T00:00:00.000Z"
}{
"id": "drift-uuid-...",
"tenantId": "your-tenant-id",
"conditionType": "vendor_contract",
"oldValue": "VendorX (active)",
"newValue": "VendorX (terminated)",
"effectiveDate": "2026-07-01T00:00:00.000Z",
"declaredBy": "admin@company.com",
"createdAt": "2026-07-03T14:30:00.000Z"
}conditionsAtAttestation includes vendor_contract: VendorX (active) will be automatically re-suspended with trigger type environmental_drift. The admissibility boundary will deny access inline until re-attested.Reject Suspension (Permanent Revoke)
Permanently revoke access. Structural tuples are deleted asynchronously by the cleanup worker.
POST /api/v1/tenant/governance/suspensions/:id/reject{
"reason": "User no longer requires billing access after transfer to Marketing. Access permanently revoked per SOC2 least-privilege policy."
}{ "message": "Suspension revoked" }revoked and remains until the TupleCleanupWorker confirms all structural tuples are deleted from SpiceDB. The subject cannot regain access during this window.AuthZEN Response When Governance Denies
When the admissibility boundary denies a request, the AuthZEN response includes structured context:
{
"decision": false,
"context": {
"reason_admin": {
"code": "ADMISSIBILITY_BOUNDARY",
"conditions_failed": ["suspension_active", "attestation_stale"],
"detail": "Execution denied: governance conditions not met at consequence time"
}
}
}{
"decision": false,
"context": {
"reason_admin": {
"code": "GOVERNANCE_SUSPENSION",
"suspension_id": "abc-123-...",
"required_attestations": ["manager_approval", "security_review"]
}
}
}suspension_active (subject is actively suspended), attestation_stale (attestation max-age exceeded), environmental_drift (declared conditions changed since attestation).Key Guarantees
- β Suspension takes effect in <500ms (O(1) β no tuple mutations)
- β Inline staleness check β zero-window enforcement, no background-only reliance
- β Revocation leak prevention β Redis key persists until tuple cleanup confirms
- β Policy-configurable failure modes (fail_open / fail_closed)
- β Non-governed subjects bypass with <0.1ms overhead
- β Immutable audit trail linking suspensions to Kafka events for forensic traceability
SDKs & Libraries
Official client libraries for integrating with AuthHub. The Node.js SDK is published on npm and supports permission checks, tuples, schema management, and OpenID AuthZEN evaluation.
Node.js / TypeScript
@auth-hub/sdk-node v0.2.0 β Connect protocol
npm install @auth-hub/sdk-nodeInstallation & Setup
import { AuthHubClient } from '@auth-hub/sdk-node';
const client = new AuthHubClient({
endpoint: 'https://api.authhub.cloud',
apiKey: 'ahk_your_key_here',
apiSecret: 'ahs_your_secret_here',
tenantId: 'your-tenant-id',
subTenantId: 'your-sub-tenant-id',
});Permission Checks
const { allowed } = await client.check({
subject: { type: 'user', id: 'dr_mehta' },
permission: 'view_record',
resource: { type: 'patient', id: 'NHS_9876543210' },
});
if (allowed) {
// User has access
}const results = await client.checkBatch([
{ subject: { type: 'user', id: 'bob' }, permission: 'view', resource: { type: 'doc', id: '1' } },
{ subject: { type: 'user', id: 'bob' }, permission: 'edit', resource: { type: 'doc', id: '1' } },
]);
// results[0].allowed β true, results[1].allowed β falseAuthZEN Evaluation (OpenID AuthZEN 1.0)
The SDK supports the OpenID AuthZEN standard using the SARC (Subject, Action, Resource, Context) model. Evaluation returns an AuthZEN-compliant response with decision and optional obligations for break-glass access.
const result = await client.evaluate({
subject: { type: 'practitioner', id: 'P123' },
action: { name: 'view_record' },
resource: { type: 'patient', id: 'NHS_9876543210' },
});
console.log(result.decision); // true or falseconst result = await client.evaluate({
subject: { type: 'practitioner', id: 'P123' },
action: { name: 'view_record' },
resource: { type: 'patient', id: 'NHS_9876543210' },
context: {
agent: {
type: 'clinical_decision_support',
id: 'cds-agent-001',
name: 'CardioAssist AI',
},
},
});const result = await client.evaluate({
subject: { type: 'practitioner', id: 'P456' },
action: { name: 'view_sensitive' },
resource: { type: 'patient', id: 'NHS_1234567890' },
});
if (!result.decision && result.context?.obligations) {
// Break-glass opportunity available
const obligation = result.context.obligations[0];
console.log('Emergency access at:', obligation.uri);
console.log('Required fields:', obligation.required_fields);
console.log('TTL:', obligation.context.ttl_seconds, 'seconds');
// Fulfill the obligation:
// POST /api/v1/break-glass { event_id, justification, authorising_clinician }
}// Send a FHIR/HL7 payload β the COAZ mapper translates it to SARC
const result = await client.evaluate(
fhirPayload as any,
{ coazMapping: 'epic-fhir-r4' }, // Active COAZ mapping name
);Relationship Tuples
// Write a tuple
await client.writeTuple({
resource: { type: 'patient', id: 'NHS_999' },
relation: 'treating_clinician',
subject: { type: 'user', id: 'dr_mehta' },
});
// Write with indirect subject (group membership)
await client.writeTuple({
resource: { type: 'ward', id: 'cardiology' },
relation: 'nurse',
subject: { type: 'group', id: 'SG_Cardiology_Nurses', relation: 'member' },
});
// Delete a tuple
await client.deleteTuple({
resource: { type: 'patient', id: 'NHS_999' },
relation: 'treating_clinician',
subject: { type: 'user', id: 'dr_mehta' },
});
// List tuples (paginated)
const { items, total } = await client.listTuples(1, 50);Schema Management
// Deploy a schema
const { zetaToken } = await client.deploySchema(`
definition user {}
definition patient {
relation treating_clinician: user
relation ward_nurse: user
permission view_record = treating_clinician + ward_nurse
}
`);
// Read current schema
const schema = await client.readSchema();
// Dry-run (validate without deploying)
const diff = await client.dryRunSchema(newSchema);
console.log(diff.additions); // New types/relations
console.log(diff.breaking_changes); // Breaking changesExpress Middleware Example
import express from 'express';
import { AuthHubClient } from '@auth-hub/sdk-node';
const app = express();
const authz = new AuthHubClient({ /* config */ });
app.get('/patients/:nhsNumber/record', async (req, res) => {
const { allowed } = await authz.check({
subject: { type: 'user', id: req.user.id },
permission: 'view_record',
resource: { type: 'patient', id: req.params.nhsNumber },
});
if (!allowed) {
return res.status(403).json({ error: 'Access denied' });
}
// Proceed with record access...
});Error Handling
import { AuthHubClient, AuthHubError } from '@auth-hub/sdk-node';
try {
await client.check({ /* ... */ });
} catch (err) {
if (err instanceof AuthHubError) {
console.error(`AuthHub ${err.method} ${err.path} failed (${err.statusCode}): ${err.message}`);
}
}Configuration Reference
| Option | Required | Default | Description |
|---|---|---|---|
| endpoint | β | β | AuthHub API URL |
| apiKey | β | β | API Key ID |
| apiSecret | β | β | API Secret |
| tenantId | β | β | Tenant ID |
| subTenantId | β | β | Sub-tenant ID |
| namespacePrefix | auto | Namespace prefix for type isolation | |
| timeoutMs | 10000 | Request timeout (ms) |
Other Languages
Python
authhub-sdk β asyncio & sync
.NET
AuthHub.Sdk β .NET 8+ with DI
Using HTTP directly
If no SDK is available for your language, you can call AuthHub via HTTP/JSON directly. Any HTTP client that supports Bearer auth can call the endpoints documented in the API Reference.
npm Package
https://www.npmjs.com/package/@auth-hub/sdk-node
