Authentication suites
- Why the harness owns the identity providers
- What is tested
- Capabilities
- Where tokens come from
- Run it yourself
- Against a third-party server
Why the harness owns the identity providers
The interesting authentication tests are the negative ones. Does the server refuse a token that is expired, signed by the wrong key, meant for another audience, or signed with alg: none? Does the authorization server refuse a credential whose signature does not verify, or an ID Token from a provider the subject never named? And does the storage answer 401 with a proper WWW-Authenticate challenge? A real identity provider will not mint broken credentials on request, so Touchstone brings its own, and makes each one with exactly one defect. The definitions name these as identities (alice-expired, didkey-bad-signature, oidc-untrusted-issuer); definitions/lws10/identities.yamlld says what each defect is.
What is tested
| Area | Tests | What they exercise |
|---|---|---|
Storage authorization (core/storage_authorization) | 19 | The 401 challenge and the protected resource metadata it names (RFC 9728), the owner’s access, a non-owner refused, and the access-token negative matrix: expired, not yet valid, issued in the future, wrong audience, wrong issuer, corrupted signature, unknown key, alg: none. |
Authorization server (core/authorization_server) | 9 | RFC 8414 metadata at /.well-known/lws-configuration, with the subject token and identifier types it lists, and RFC 8693 token exchange: a valid exchange, an unknown or missing resource, a missing subject token, and the storage accepting the token it issued. |
Access grants (core/access_grants) | 17 | The access grant service, public and per-agent grants, the grant document and its constraints, an incomplete grant refused, revocation, a grant announced in its inbox, access requests. |
OpenID Connect (auth/oidc) | 6 | Token exchange with an ID Token from the harness’s OpenID Provider, which the subject’s controlled identifier document names, and five defects, including a provider the subject never named. |
Controlled Identifiers (auth/cid) | 22 | Two kinds of subject. With an HTTPS subject (authn-cid-*): token exchange with a self-issued credential whose key the verifier finds in a CID document the harness hosts, embedded or referenced by id, and nine defects, including a kid that names no key, a revoked key and a key another identifier controls. With a did:key subject (authn-cid-didkey-*), whose DID document the verifier derives from the identifier: token exchange and ten defects: signature, alg: none, expired, mismatched claims, an audience without the authorization server, no exp, no iat, no sub, no iss, no client_id. |
SAML 2.0 (auth/saml) | 3 | Token exchange with a signed assertion from the harness identity provider, an altered one, and an unsigned one. |
All of them pass against the secured reference deployment, and the self-test loop shows that each negative test fails against a broken twin that accepts what it should refuse.
Capabilities
What a test needs from the deployment, beyond the server itself, is a capability the target declares. A test that needs one the target lacks is inapplicable.
| Capability | Meaning | The target provides |
|---|---|---|
Authentication | The storage challenges anonymous requests, and alice and bob are distinct authenticated agents. | Tokens for alice and bob (see below), and their CID URLs as cidUrl.alice and cidUrl.bob when grants name them. |
HarnessIssuedTokens | The storage trusts an authorization server whose signing key the harness holds, so the harness can mint valid tokens with one chosen defect. | as.signingKey: the authorization server’s private JWK. |
ReachableFixtures | The target can reach the harness’s fixture host, which serves the CID and OpenID documents it dereferences. | fixtures.baseUrl: the URL the target uses for it, and fixtures.bind if the harness must listen elsewhere. |
SamlTrust | The authorization server trusts the harness identity provider’s signing key. | saml.idpKey: an RSA private JWK, and saml.idpCertificate if the trust is by certificate. |
Where tokens come from
alice and bob send Authorization: Bearer <token>, from the first source that applies:
- Minted by the harness, with
HarnessIssuedTokens: an RFC 9068 access token signed with the authorization server’s key, for the storage the storage’s401links to. - Exchanged, when
didkey.jwk.<name>gives the identity a P-256 did:key: the engine exchanges a CID-suite credential for that DID subject at the token endpoint, exactly as the token-exchange tests do. - Static, from
token.<name>orTOUCHSTONE_TOKEN_<NAME>. - None, on a target that does not declare
Authentication.
A fault identity starts from its basis’s token. Three faults need no key, so they apply to any target with real JWTs: a corrupted signature, alg: none, and a re-signature by a key nobody publishes. The others need HarnessIssuedTokens, except Expired, which without it waits for a real token to expire, when that is at most ten minutes away.
The subject credentials of the three suites are minted per test: for CID, a P-256 key per run behind either a did:key (its DID document derived, nothing hosted) or a document the harness hosts, ID Tokens from the harness’s own OpenID Provider (and from a rogue one), and SAML assertions signed with enveloped XML Signature by the JDK’s own XML Signature API.
Run it yourself
The secured reference deployment starts the reference authorization server and a reference storage that trusts it, and writes a target registry that declares all four capabilities. Build once (./mvnw -q clean install -DskipTests), then start it from the repository root:
./mvnw -q -pl harness-fixtures exec:java \
-Dexec.mainClass=com.ebremer.touchstone.fixtures.SecuredRefScenarioMain \
-Dexec.args=targets-secured.yaml
The command writes targets-secured.yaml to the repository root; Git ignores that file, which holds throwaway keys. It prints the two servers’ addresses and keeps running. In a second terminal:
touchstone run --target secured-ref --targets targets-secured.yaml --module auth
[passed ] MUST auth/oidc/manifest#authn-oidc-valid-id-token (381 ms)
[passed ] MUST auth/oidc/manifest#authn-oidc-expired-id-token (206 ms)
[passed ] MUST auth/oidc/manifest#authn-oidc-alg-none (189 ms)
...
[passed ] MUST auth/cid/manifest#authn-cid-didkey-valid-credential (300 ms)
...
31 passed, 0 failed, 0 cantTell, 0 inapplicable (target secured-ref, run 5759ac9a)
conformant: no MUST test failed or ended cantTell
Leave out --module to run all 219 definitions against it.
Key rotation during a session is tested at fixture level: after the authorization server retires its signing key, a token that was valid stops validating.
Against a third-party server
- Storage authorization. With real tokens for alice and bob as
TOUCHSTONE_TOKEN_ALICEandTOUCHSTONE_TOKEN_BOB, declareAuthentication. The challenge, owner and non-owner tests apply, and so do the three signature faults if the tokens are JWTs. The other faults needHarnessIssuedTokens, which only a deployment whose authorization server shares its key with the harness can declare; the self-test loop shows those tests can tell a compliant server from a broken one. - Token exchange and did:key subjects. A did:key needs no trust set up in advance and nothing the server must reach, so the authorization server tests and the CID suite’s did:key-subject tests apply to any server whose metadata lists
did:keyamong its subject identifier types. They are inapplicable otherwise. - CID with an HTTPS subject, and OpenID Connect. The authorization server must reach the harness’s fixture host: declare
ReachableFixturesand setfixtures.baseUrlto the harness’s address as the server sees it. - SAML. The authorization server must trust the harness identity provider: declare
SamlTrust, give its key assaml.idpKey, and configure the server to trust it.
Targets and credentials shows the registry entries.