Portable authorization and fragments — implementation foundation

Status: local reversible implementation, 2026-10-11. Human review and production sign-off remain pending. This service does not expose owner-authorized demonstration routes as authenticated federation routes.

packages/federation/index.mjs exports FederationService, FederationFault, and an in-memory reference transaction store. Runtime integrations supply a serializable, rollback-capable store.transaction(operation) with transaction get, set, delete, and list(prefix) returning {key,value} records. Schema, resource, request, code, token, preference, and grant records share that transaction domain. A disconnected KV adapter is insufficient. Code consumption and bearer issuance occur atomically. Bearer and code records are indexed by domain-separated SHA-256 hashes, never plaintext bearer/code values.

The constructor accepts {store, crypto, clock, issuer, clients, schemaFetcher, schemaHosts, authenticateOwner, dispatchCapability}. Issuer is an HTTPS origin. Static operator-reviewed clients have {id,name,origin,redirectUris}: ID is HTTPS, origin is exact HTTPS, redirects are an exact preregistered allowlist on that origin. This implementation does not claim to verify application domain control or implement dynamic registration. Configuration provenance requires operator review.

authenticateOwner(authority,transaction) must verify the current authenticated, active provider account and return {subject} on the configured issuer. The argument is an injected runtime authority such as a live server session, never a caller-supplied subject string. Missing authentication fails closed. HTTP integration separately performs exact Origin, CSRF, framing protection, secure cookie handling, and session verification. Authentication alone creates no grants.

schemaFetcher(url) is a trusted injected runtime capability, not an ambient network fetch. schemaHosts is an exact host allowlist. The fetcher must impose response/time limits, reject redirects, validate public addresses after every DNS resolution, and return parsed JSON. An operator-local immutable schema registry is also valid. The service bounds canonical JSON to 64 KiB, accepts at most 32 fields, rejects unknown/executable properties, restricts primitive/cardinality/range values, and pins canonical SHA-256 digests. String/URL fields require explicit maxLength; numeric fields require finite minimum/maximum. Existing URLs cannot change. No schema URL triggers network access unless the runtime deliberately supplies a trusted fetcher.

Runtime API

dispatchCapability({transaction,subject,clientId,resource,contract,method,args,tx}) receives only previously authorized requests. It must perform contract argument/result validation, resource isolation, durable idempotency, atomic object preconditions, and indeterminate-outcome handling. This foundation is an authorization gateway, not a claimed object-store implementation. It does not manufacture successful profile/object-storage results. Runtime integration must provide actual contract implementations and independent adapter conformance before claiming the baseline release gate. Holding a local store transaction does not itself make an external provider write atomic.

Consent integration

Serve apps/web/authorize.html for /oauth/authorize?request_id=.... Its dodo UI uses genuine ngin Action/Query resources. GET /api/session returns csrfToken; GET /api/oauth/requests/:id returns inspect's owner-specific view. POST /api/oauth/requests/:id/decision forwards the CSRF-protected decision. POST /api/preferences forwards an authenticated preference configuration. The HTTP layer must validate the request ID against the path and supply authority itself.

The UI preserves the request ID during sign-in, grants nothing automatically, shows registered app origin, developer purpose as unverified plain text, schema URL/publisher, every shared field value, selected resources/providers/methods, destructive-method labels, and already affected apps. Optional fragments can be declined before configuration. Saving preferences explains effects on approved apps and requires a subsequent explicit decision. The owner can cancel every connection. A stale revision requires fresh review. Resource provisioning and comprehensive persistent account settings remain runtime/account-UI integration work.

Verification and remaining gates

Independent relying-party fixture computes PKCE with Node's separate SHA-256 implementation. Tests cover atomic parallel exchanges, wrong verifier, redirect substitution, expiry, state/issuer, anonymous inspection, unauthenticated approval, stale consent, live revoke, additions/removals/restoration, and immutable schema substitution. A happy-dom fixture exercises real dodo/ngin, sign-in gate, optional decline, plain-text metadata, affected apps and CSRF decisions. These are local conformance foundations, not two deployed independent provider implementations or a reviewed production authorization server.

Open release gates include trusted network-fetch adapter, authenticated HTTP routing, production client registration provenance, independent end-to-end provider/RP interoperability, complete object/profile dispatch conformance, durable journal, browser security headers, quota/audit/retention policy, accessible full-browser verification, and human security review. Reviewer: ___ Date: ___ Outcome: ___ .

Shared runtime and hosted adapter integration

FederationController composes the service from {metadata,auth,issuer,clients,schemaFetcher,schemaHosts,dispatcher,crypto?,clock?,allowInsecureLocalhost?,enabled?}. Authentication is auth.owner(context,{mutation}); it must call the real IdentityService.authorized(context,mutation) to verify active account, current hashed session, expiry, exact origin and mutation CSRF. It returns the canonical entity subject. Controller owner methods take (context,input); inspect(context,requestId) permits anonymous public metadata only. federationMetadata(native) adapts native get/put/delete/list(Map) transactions; federationRemote(stub) preserves typed errors through an explicit {ok,result|error} RPC envelope.

handleFederation(request,{controller,issuer,clients,allowInsecureLocalhost,assets,enabled}) returns a response or null for unrelated paths. It implements configured-client CORS, bounded UTF-8/JSON and standard form token exchange, owner cookie extraction, exact provider Origin and CSRF on consent/configuration/revoke, path-bound decisions, authorization assets with denial of framing, and no-cache responses. A disabled/unconfigured controller returns 503 for discovery and authorization; a manifest never advertises a working service before configuration. JWKS contains no keys because tokens are opaque; no unsigned JWT identity claims are supplied. Explicit localhost HTTP configuration is restricted to literal localhost; remote HTTP remains rejected.

The controller journal is keyed by entity/client/UUID and fingerprints the canonical resolved resource, contract, method and args. Live grant authorization occurs before every cached outcome. Completed outcomes persist for 24 hours; ambiguous write failures persist as indeterminate and cannot automatically execute again. Adapters must declare verified transactional:true for local transactional mutations or durableJournal:true for external operations that reserve in their own durable account journal before side effects. An ordinary dispatcher function may serve reads but cannot execute mutations. A local metadata transaction cannot preserve a reservation across a process crash while an external effect is outstanding; the external/account journal is therefore mandatory. Runtime configuration must enable this assertion only after actual adapter tests.

apps/worker/federation.mjs is the only Cloudflare-specific module. FederationObject uses native storage transactions and validates owner contexts through the actual shared IdentityService.authorized method and the identity directory. Its operator configuration uses FEDERATION_ENABLED, JSON FEDERATION_CLIENTS, JSON immutable FEDERATION_SCHEMAS, optional FEDERATION_SCHEMA_HOSTS, and an explicitly enabled FEDERATION_DISPATCHER_MODE=account-journal-v1 to invoke AccountObject.federationRPC({subject,clientId,resource,contract,method,args,tx}). The account dispatcher must return a raw contract result. Credentials and caller-supplied subject shortcuts never replace owner verification. Registry-only schemas trigger no network requests.

Runtime HTTP tests use a separate relying-party SHA-256 implementation and real IdentityService session hashes/CSRF checks. They exercise the complete configured request/consent/code/token/RPC flow, replay/collision, false Origin, invalid CSRF, suspended-owner inspection, duplicate token parameters, disabled discovery, explicit localhost issuer restrictions, and durable ambiguous-outcome suppression. Production publishing remains separately gated.