Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ jobs:

- run: pnpm install --frozen-lockfile

- run: pnpm audit --audit-level=moderate

# pnpm's frozen install tolerates stale extra importers, so assert the
# invariant directly: the committed lockfile must never contain the
# local-only instances/* importers (see Makefile clean-instances).
Expand Down
59 changes: 59 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,36 @@ releases.

## [Unreleased]

### Fixed

- **Generated instances never logged NDJSON.** Both scaffolds took their logger
from `@o3co/auth.utils`, which treats pino as an *optional* peer and falls
back to `console` when the import fails — and the generator never emitted
pino, so every instance created by `create-provider` or
`create-policy-verifier` logged bare `[name] …` console lines that no
aggregator parses. The scaffold now ships its own `src/logger.mts` on pino
(a direct runtime dependency, exact-pinned like the rest), honouring
`logging.level` from the application config with `LOG_LEVEL` as the
environment override, and serialising `err` so an Error keeps its stack.

- **Generated instances could hang on SIGTERM and always exited zero.** The
same package's `gracefulShutdown` called `server.close()` with no deadline,
so one stuck request meant the process never exited on its own and the
orchestrator's SIGKILL cut it down mid-flight; cleanup failures went to
`console.error`. The scaffold now ships `src/shutdown.mts` with the contract
auth.provider (#290), auth.proxy (#81) and auth.policy-verifier (#210) each
adopted: drain for `drainTimeoutMs` (default 10s), bound `cleanup` by
`cleanupTimeoutMs`, force-close past the deadline and exit non-zero, log
through the instance logger, and yield the loop once before exiting so the
last lines flush. Both files come with tests that run under the instance's
own `pnpm run test`.

### Changed

- **`@o3co/auth.utils` is no longer emitted into generated `package.json`.**
Its two helpers live in the scaffold (above). This was the package's last
consumer across the auth family.

### Added

- The OWNER login contracts (`OWNER_AUTHENTICATION_LOGIN@1`,
Expand Down Expand Up @@ -39,6 +69,35 @@ releases.

### Changed

- **The auth baseline is the released upstream, not a 0.3.x / 0.5.x pin with a
compatibility shim.** Every workspace package now requires
`@o3co/auth.policy-verifier.{core,builtins,server}` `^0.8.1` and
`@o3co/auth-provider-{core,oauth}` `^0.12.0` — the versions published on
2026-09-06 — and the generators emit the same as exact pins
(`DEFAULT_DEP_VERSIONS`: 0.8.1 / 0.12.0), with both generators bumped to
0.2.0 per create-app.md § 3.3. The dual-path shim that let the collectors
read `payload` or `subject` and reach `readUntrustedRequestContext` by
reflection (`collectors/context.mts`) is removed: collectors read
`context.subject` and call `readUntrustedRequestContext` directly, and the
policy-verifier template and the integration test import
`builtinKeyResolversModule` rather than probing for it. The code had already
crossed the intervening upstream BREAKING changes (o3co/auth's
`provin-compatibility` job builds this workspace against those exact
revisions); what changes here is that the released-0.3.x branch of each
dual path is gone. `@o3co/ts.hocon` stays at its current pin — its 0.1 → 1.x
move is a separate migration.

- Refresh vulnerable transitive lockfile entries: js-yaml 4.3.2, qs 6.16.0,
nanoid 3.3.18 and brace-expansion 5.0.9. CI audits the dependency graph.
- Prepare generated instances for current upstream auth while retaining released
dependency pins: align Zod 4.5.4, wire separated JWKS/key-resolver modules,
update required configuration and support verified subject bags plus explicitly
untrusted request context.
- Generated Verifiers now require an explicit Owner DID rule on the declared
surface instead of relying on empty-rule allow. Scopeless DID tokens skip only
the scope group; undeclared operations and non-Owner subjects remain denied.
See [upstream compatibility](docs/upstream-compatibility.md) for migration.

- `login-transcript-v1` (unreleased) gains an eleventh required field,
`did`, alongside the existing `subject_did` — `validateOwnerLogin` now
also checks `transcript.did === transcript.subject_did`. `did` is the
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ dPLaaX protocol: libraries plus scaffold generators that produce per-deployment
composition roots of [auth.provider](https://github.com/o3co/auth.provider) and [auth.policy-verifier](https://github.com/o3co/auth.policy-verifier).

See [docs/requirements.md](docs/requirements.md) for what this repository provides.
See [upstream compatibility](docs/upstream-compatibility.md) before adopting
current auth-family candidate builds in an existing generated deployment.

> **Lineage**: this repository's history starts at the public cut, not at the
> start of the work. The code grew up in a private PoC auth stack for dPLaaS,
Expand Down
2 changes: 1 addition & 1 deletion docs/requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,6 @@ Upstream packages composed by this repository:

- Provider: `@o3co/auth-provider-core`, `@o3co/auth-provider-oauth`, `@provin-line/auth-provider-did`
- Policy-verifier: `@o3co/auth.policy-verifier.server`, `@o3co/auth.policy-verifier.builtins`, `@o3co/auth.policy-verifier.core`
- Shared: `@o3co/auth.utils`, `@o3co/ts.hocon`
- Shared: `@o3co/ts.hocon`; generated instances log through `pino` directly (their logger and shutdown ship inside the scaffold)

Version constraints follow each service's `package.json`.
52 changes: 52 additions & 0 deletions docs/upstream-compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Upstream auth compatibility

Generators still pin released provider 0.5.3 and verifier 0.3.1. Updating this
repository's git ref alone does **not** deploy newer upstream security fixes.
Existing instances need their dependency baseline and configuration updated.

The [o3co/auth compatibility suite](https://github.com/o3co/auth) tests candidate
source without publishing packages: it packs provider/core, provider/oauth and
verifier/core, builtins and server into local tarballs, overrides all matching
direct and transitive dependencies in a disposable Provin checkout, then runs
workspace build/typecheck/tests, generated-app build/typecheck/config tests,
service startup and a generated Provider's valid/tampered DID-signature grant.
This does not certify deployed registry ACLs or Web/mobile clients.

## Adopting current upstream

- Keep Zod at one minor version across the dependency graph. Workspace and
generator overrides pin 4.5.4: mixing 4.3.x and 4.5.x schema objects across
module boundaries fails TypeScript compilation.
- Supply an absolute Provider issuer. Set the Verifier's `OAUTH_JWT_ISSUER`
and `OAUTH_JWT_AUDIENCE`. DID clients must include that audience in their
**signed** message/transcript. Current Verifier rejects LEGACY tokens with no
`aud`, although the grant still accepts audience-absent LEGACY requests.
- Use `oauth.jwt.mode = "verify"`; remove old `validate` and
`allowInsecureDecode` keys from overlays. Supply strong signing keys/secrets.
- Generated Provider config supplies required `http.readinessTimeoutMs` and
`logging.level`. `DplaaxConfigSchema` preserves the audit declaration.
- This DID-only composition has no session/password store, token denylist or
audit sink. Config explicitly declares subject/access-token revocation
unsupported and audit sink absent. Lifecycle is checked at issuance; issued
tokens remain usable until bounded expiry. Earlier revocation or retained
audit events requires wiring those services.
- Current Provider separates JWKS publication from OAuth; current Verifier
separates key-resolver registration. Composition includes those modules when
available; released versions retain their internal wiring.

## DID policy and trust boundaries

The scaffold now requires an explicit Owner DID rule on its declared surface.
Scopeless tokens skip only the OAuth scope group.
`DefaultDenyRuleCollector` still rejects undeclared resource/action pairs.
Other configured groups, including subscriber identity when enabled, must pass.
A missing or non-Owner subject is denied.

This avoids relying on 0.3.x's empty-rule allow behavior; current upstream denies
empty rules. PDP allow is the identity/surface gate; resource permissions remain
the downstream registry's ACL decision, as required by the Provin contract.

Collectors use verified `subject` on current upstream and `payload` on 0.3.x.
When present, `subject` is authoritative even if empty. Subscriber fields remain
caller supplied: current upstream reads them only through
`readUntrustedRequestContext`, with no plain-record fallback.
9 changes: 5 additions & 4 deletions integration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
},
"devDependencies": {
"@noble/ed25519": "^3.1.0",
"@o3co/auth-provider-core": "^0.5.3",
"@o3co/auth.policy-verifier.builtins": "^0.3.1",
"@o3co/auth.policy-verifier.server": "^0.3.1",
"@o3co/auth-provider-core": "^0.12.0",
"@o3co/auth.policy-verifier.builtins": "^0.8.1",
"@o3co/auth.policy-verifier.server": "^0.8.1",
"@provin-line/auth-provider-did": "workspace:*",
"@provin-line/auth-provider-dplaax-module": "workspace:*",
"@provin-line/auth-policy-verifier-dplaax-module": "workspace:*",
Expand All @@ -22,6 +22,7 @@
"express": "^5.2.1",
"jose": "^6.2.2",
"typescript": "^5.9.3",
"vitest": "^4.1.4"
"vitest": "^4.1.4",
"@o3co/auth.policy-verifier.core": "^0.8.1"
}
}
Loading
Loading