Repository navigation
v0.5.0 — auth.provider 0.18.4 provider (BREAKING: generator, settings, image variables); 0.15 packages removed; policy-verifier 0.17.0 baseline; kid-match under every contract; token-control
LatestA minor number, not a patch: the provider moves to auth.provider 0.18.4.
Its generator now calls upstream's, and the DID grant, the registry
resolver and the nonce store are 0.18 modules. Every provider deployment
changes its settings, its image's variables and its records, and the 0.15
packages are removed. The policy-verifier baseline moves to 0.17.0, which
changes the AppConfig type. Consumers that track the moving v0.4 image
tag are unaffected; provin.oss's quickstart pins v0.3 and moves to v0.5
on its own schedule.
Tokens from the DID grant carry the six signed claims of
auth.token.signed-claims, the generated provider answers revocation and
introspection, and rule auth.grant.kid-match holds under every contract.
docs/upstream-compatibility.md lists what
an existing provider or verifier instance changes.
Added
@provin-line/auth-dplaax-registry: provides the DID grant's
didDocuments(packages/dplaax-registry).- It resolves did:dplaax owner DIDs at
<url>/did/<accountType>/<accountId>/did.jsonthrough auth.provider's
outbound, as the operator's own URL. The operator's destination policy
decides between https (outbound.internal-hosts) and plain http to a
private address (outbound.plain-internal). - Its section
dplaax-registryreplaces 0.15'sdplaax.registry.baseUrl
/allowedRegistriesandoauth.grants.did.resolver.*:url(now
required; 0.15 defaulted tohttps://registry.dplaax.dev),
allowed-registries,timeout(5s) andmax-document-size(64KiB,
withinoutbound.max-response-size; 0.15 took 1 MiB). - Concurrency is outbound's
max-concurrent(16, shared with every
outbound caller): a request past it is refused at once (503), where 0.15
queued it behind its own limit of 8. - Outcomes: rejected for a DID the registry does not hold (404 or another
4xx) or bytes that are no document for it; unavailable for 5xx, no
answer in time, a refused destination (a redirect included) and an
answer over the size limit. outbound stops a large answer before its
status is read, so an oversized document is now unavailable (503), where
0.15 rejected it (400). - Decoding meets rule
canon.strict-decode: invalid UTF-8 and unpaired
surrogate escapes are refused, and integers beyond 2^53 are kept
exactly.strictJsonParseis internal to this package.
- It resolves did:dplaax owner DIDs at
@provin-line/auth-did-grant-store: keeps the DID grant's spent nonces
onrecordsover the kernel'sstore(packages/did-grant-store), with
the guaranteesspentNoncesNeedsasks, as upstream'sjwt-bearer-store
does.@provin-line/auth-did-grant: the DID grant as an auth.provider 0.18
module (packages/did-grant), replacing the 0.15auth-provider-did.- Its tokens carry the six claims of rule
auth.token.signed-claims
(auth_contract_id,verification_method,did_document_snapshot,
lifecycle_state_ref,lifecycle_freshness_ref,
authorization_scope) through auth.provider's added claims, signed
into the JWT.client_idnames the client;azpis no longer written. - The package exports the contract at
., the module at./moduleand
spentNoncesContractat./testing. auth.provider's packages and zod
are peers. - It takes two contracts it owns:
didDocuments(the resolver) and
spentNonces(the nonces already used). Custom signature algorithms
are contributed atsignatureAlgorithmsinstead of an injected
VerifierRegistry, and say whether they carry a JWS header; the grant
issues only when a verifier's subject and message are the requested
DID's. Verifiers are made at boot, so a missing@noble/ed25519
refuses boot instead of failing every request. es256k_jwsis no longer offered: jose does not verify ES256K on Node,
so it never worked. A deployment that needs it contributes a verifier.- Its section
did-grantreplacesoauth.grants.did: lifetimes carry a
unit, andaccess-token-lifetimeis the grant's own. Audiences and
scopes come from the client'sclient-allowance(allowedAudiencesis
gone), and the OWNER token endpoint comes from auth.provider's
tokenEndpointLocation(tokenEndpointis gone). - A failed signature is
400 invalid_grant, never401. A resolver or
nonce store that cannot tell answers503. - A message's nonce is kept until the message's own window closes, plus a
minute for clock differences. It was kept formessage-max-agefrom
the request, which left a message stamped ahead of the clock usable
once more.
- Its tokens carry the six claims of rule
Changed
- BREAKING (
@provin-line/create-auth-provider0.4.0): the generated provider is an
auth.provider 0.18.4 app. The generator calls auth.provider's own
(@o3co/auth-provider-scaffold, pinned at exactly 0.18.4) for a listed app
of@mod/token-endpoint,@mod/client-allowance,@mod/outbound,
@mod/discoveryand@mod/token-control, then composes@provin-line/auth-did-grant,
auth-dplaax-registryandauth-did-grant-storeinto it
(packages/create-provider/src/overlay/, one function per patched file).- The 0.15 template is gone. The app runs its sources
(node src/main.ts), with no build, notsconfig.jsonand nodist/;
its settings are env variables read byconfig/application.conf, a local
layer (APP_CONFIG) for lists, and a refused boot exits 78. Existing
0.15 instances do not carry over: auth.provider 0.18 reads different
keys and records. - Revocation (
POST /oauth/revoke) and introspection
(POST /oauth/introspect) are@mod/token-control's.- Introspection takes client authentication only: a
confidential
client with aclient-secret.hashwhoseclient-allowance.audiences
covers the token's audience (the issuer URL for a DID client with no
audiences). 0.15's self-introspection, with the caller's own access
token as its credential, is gone, and a public client gets401. - With
tokens.revocation = denylist, introspection answers a revoked
token inactive. Withunsupported,/oauth/revokeanswers
400 unsupported_token_typefor access tokens and the metadata names
norevocation_endpoint; 0.15 always kept a denylist. - The provin policy-verifier reads the JWT alone and does not see a
revocation: a revoked token passes it until it expires, within
did-grant.access-token-lifetime(provin-line/oss#85).
- Introspection takes client authentication only: a
- Added to upstream's app: the did-grant, dplaax-registry and
keys.signing.secret/secret-encodingkeys and variables (a
deployment sharing an HS256 secret with a policy-verifier sets
utf8), the DID grant type in the example client's allowance, the
registry inoutbound.allowandoutbound.plain-internal, and
tokens.subject-check(boot requires it, or a module that checks
subject revocation, which this app does not compose). pnpm-workspace.yaml(pnpm 11 no longer readspackage.json#pnpm)
pins@provin-line/did-dplaaxand@provin-line/auth-did-grantto the
same ref, setsblockExoticSubdeps: falsefor those git sources below the
app's direct dependencies, and allows the git-fetched packages their
preparethroughallowBuilds, keyed by the source pnpm resolves
(docs/create-app.md § 3.4). For a branch or tag under
--allow-unpinned-refnoallowBuildsis written and the first install
names the key to add.- Removed CLI flags and exports:
--port(the port isHTTP_PORT),
--package-manager(upstream's app is pnpm), andDEFAULT_PORTand
DEFAULT_PACKAGE_MANAGERfrom the package.DEFAULT_DEP_VERSIONSnow
holds only what the overlay adds (@noble/ed25519), and
LISTED_MODULESis new.--registry-base-urlis the default
DPLAAX_REGISTRY_URLof.env.example. - Runtime contract of the published
auth-providerimage. It runs
the generated app, so the 0.15 variables (OAUTH_JWT_*,
DPLAAX_REGISTRY_BASE_URL,CLIENT_REPOSITORY_PATH,HTTP_PORTaside)
no longer mean anything. A container sets the 0.18 variables
config/application.confreads (KERNEL_DEPLOYMENT,
STORE_ACCEPT_VOLATILE,TOKENS_REVOCATION,TOKENS_SUBJECT_CHECK,
HTTP_PUBLIC_ORIGIN,HTTP_INSECURE_ORIGIN,ISSUER_URL,
KEYS_SIGNING_*,DID_GRANT_*,DPLAAX_REGISTRY_URL) and mounts a local
layer of lists (outbound.allow,audit.receivers,
clients.registered) named byAPP_CONFIG
(config/local.conf,config/application.conf); the dev composition's
deploy/dev/provider.local.confis an example. The image listens on
HTTP_HOST=0.0.0.0andHTTP_PORT=3000(the app's own default is
8080).--registry-base-urlinpublish-images.ymlnow only fills the
generated examples; the registry at runtime isDPLAAX_REGISTRY_URL. - The scaffold smoke, CI and image build follow:
make smokeboots the
provider's sources against a throwaway DID registry on loopback and
checks a DID-grant round trip (HS256 over the secret's UTF-8 bytes, the
six signed claims, a tampered signature rejected) and the token's
introspection, revocation and introspection again; its ports are
PROVIDER_PORTandVERIFIER_PORT. The root workspace overrides map the
three new packages toworkspace:*.Dockerfile.instance,
deploy/generated-instance.Dockerfileandpublish-images.ymltake an
ENTRY(src/main.tsfor the provider,dist/main.mjsfor the
policy-verifier) and build only if the instance has a build script. The
devdocker-compose.ymlconfigures the provider with the 0.18 variables
anddeploy/dev/provider.local.conf.
- The 0.15 template is gone. The app runs its sources
- BREAKING (types, verifier packages): the policy-verifier baseline is
0.17.0. 0.17.0 makessecretEncodingrequired in theAppConfig
type'soauth.jwtand inHs256Rotation, and the dPLaaX verifier module
requires^0.17.0, so a consumer moves its three verifier packages
together. Configuration read throughAppConfigSchemais unaffected.
Workspace packages require
@o3co/auth.policy-verifier.{core,builtins,server}^0.17.0, and
create-policy-verifier(now 0.4.0) emits the same as exact pins.- 0.17.0 adds
oauth.jwt.secretEncoding, how the HS256 secret becomes
key bytes. The default,"utf8", is the reading earlier verifiers
applied, so nothing changes for a deployment that does not set it. The
generated verifier now writessecretEncoding = "utf8", overridable
withOAUTH_JWT_SECRET_ENCODING, the reading the generated provider is
configured with (keys.signing.secret-encoding = utf8). - 0.16.3 reads the client from
client_idas well asazp, which
auth.provider 0.18's tokens need. - The generated verifier also emits
@o3co/ts.hocon1.14.3 (was 0.1.5),
pino 10.4.0,@types/node26.6.5 and vitest 5.0.5. These are the newest
releases inside the ranges upstream's own verifier template declares. - docs/upstream-compatibility.md
lists what an existing instance changes.
- 0.17.0 adds
- The workspace toolchain follows upstream auth.provider 0.18.4: pnpm
11.28.7, TypeScript 6.0.3, vitest 5.0.5,@types/node26.6.5.- The base tsconfig names
types: ["node"], because TypeScript 6 no
longer loads every@typespackage by default. engines.nodeis>=24.22.1with no upper bound, and CI builds and
tests on Node 24.22 and 26.- Other dependencies move to: jose 6.2.13,
@noble/ed255193.2.0, zod
^4.6.7(still overridden to 4.6.7).
- The base tsconfig names
- pnpm settings move from
package.json#pnpmtopnpm-workspace.yaml.overrideskeeps the same entries, with vite raised to^8.3.3.minimumReleaseAgeExcludelets a fresh@o3co/*release install
despite pnpm 11's one-day hold.
Removed
- BREAKING:
@provin-line/auth-provider-didand
@provin-line/auth-provider-dplaax-module, the auth.provider 0.15
composition (packages/provider-did,packages/provider-dplaax-module).
No generator has emitted them since create-provider 0.4.0, and the
integration and conformance suites now run on the 0.18 modules.- The DID grant is
@provin-line/auth-did-grant, with its section
did-grantreplacingoauth.grants.did. - The registry resolver (
DplaaxDidResolver) is
@provin-line/auth-dplaax-registry, with its sectiondplaax-registry
replacingdplaax.registry.*andoauth.grants.did.resolver.*. - The nonce store is
@provin-line/auth-did-grant-store. buildModules,DplaaxAppConfigandDplaaxConfigSchemahave no
successor: a provider is a kernelassembleof modules, which the
generator writes.- docs/upstream-compatibility.md
lists what an existing 0.15 deployment changes.
- The DID grant is
- The
releaseworkflow, whose only job publishedauth-provider-did(and
was disabled).
Security
@provin-line/auth-did-grant: ruleauth.grant.kid-matchholds under
every contract,LEGACY_DID_LOGIN@1included. A JWS protectedkidand a
message'sverification_methodare compared, as exact strings, with the
absolute id of the resolved method that verified the signature;
any other value is refused with400 invalid_grant, its nonce unspent. A
relative reference such as#key-1, or a kid in any other spelling, is
refused even when it denotes the same key. Akidorverification_method
that is not a string, and a message that is not a JSON object, are
400 invalid_request. A message that names no method is still taken. The
0.15auth-provider-didchecked these under an OWNER contract only, and
accepted other spellings under LEGACY.- body-parser 2.4.0 in the lockfile (GHSA-27r3-jxmh-p828:
denial of service via trailing bytes after a compressed request body). It
is reached through express under the verifier server.