rodauth-ciba 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +34 -0
- data/LICENSE +21 -0
- data/README.md +106 -0
- data/docs/adr/0003-follow-rodauth-hook-conventions.md +9 -0
- data/docs/adr/0004-retain-atomic-issuance-and-stage-alignment.md +16 -0
- data/docs/alignment-closeout.md +136 -0
- data/docs/alignment-roadmap.md +160 -0
- data/docs/api.md +337 -0
- data/docs/authentication-claims.md +80 -0
- data/docs/authorization-details.md +50 -0
- data/docs/claims.md +46 -0
- data/docs/failure-contract.md +34 -0
- data/docs/id-token-encryption.md +288 -0
- data/docs/id-token-hint.md +174 -0
- data/docs/login-hint-token.md +35 -0
- data/docs/max-age.md +65 -0
- data/docs/node-alignment.md +336 -0
- data/docs/operations.md +130 -0
- data/docs/outbound-http.md +52 -0
- data/docs/pairwise.md +76 -0
- data/docs/ping.md +118 -0
- data/docs/protocol-coverage.md +114 -0
- data/docs/refresh-tokens.md +151 -0
- data/docs/release-readiness.md +75 -0
- data/docs/release-validation.md +498 -0
- data/docs/request-context.md +51 -0
- data/docs/requirements-audit-current.json +3221 -0
- data/docs/requirements-audit-current.md +424 -0
- data/docs/requirements-audit.json +3190 -0
- data/docs/requirements-audit.md +175 -0
- data/docs/research/access-token-revocation-reference.md +81 -0
- data/docs/research/alignment-partial-triage.md +50 -0
- data/docs/research/claims-reference-contract.md +33 -0
- data/docs/research/client-assertion-algorithm-contract.md +177 -0
- data/docs/research/completion-error-boundaries.md +56 -0
- data/docs/research/consent-api-evolution.md +59 -0
- data/docs/research/dpop-reference-contract.md +423 -0
- data/docs/research/id-token-hint-reference-contract.md +254 -0
- data/docs/research/mtls-reference-contract.md +449 -0
- data/docs/research/node-ciba-lifecycle.md +30 -0
- data/docs/research/node-oidc-provider-comparison.md +45 -0
- data/docs/research/pairwise-reference-contract.md +264 -0
- data/docs/research/ping-reference-contract.md +165 -0
- data/docs/research/rar-reference-contract.md +55 -0
- data/docs/research/refresh-reference-contract.md +85 -0
- data/docs/research/registration-reference-contract.md +567 -0
- data/docs/research/request-parameter-inventory.md +42 -0
- data/docs/research/required-parameter-inventory.md +39 -0
- data/docs/research/resource-reference-contract.md +40 -0
- data/docs/research/signed-request-reference-contract.md +113 -0
- data/docs/resources.md +55 -0
- data/docs/security-review.md +294 -0
- data/docs/signed-requests.md +162 -0
- data/docs/user-code.md +73 -0
- data/docs/validation/node-alignment-tests.txt +10 -0
- data/examples/client.rb +39 -0
- data/examples/demo_app.rb +121 -0
- data/examples/dpop_smoke.rb +169 -0
- data/examples/ecdh_id_token_smoke.rb +84 -0
- data/examples/edwards_id_token_smoke.rb +48 -0
- data/examples/edwards_smoke.rb +87 -0
- data/examples/edwards_tls_smoke.rb +73 -0
- data/examples/encrypted_id_token_smoke.rb +73 -0
- data/examples/encryption_tls_smoke.rb +123 -0
- data/examples/extensions_smoke.rb +226 -0
- data/examples/fixtures/edwards-requests.json +30 -0
- data/examples/hmac_id_token_smoke.rb +72 -0
- data/examples/migration.rb +18 -0
- data/examples/mtls_smoke.rb +395 -0
- data/examples/ping_smoke.rb +226 -0
- data/examples/registration_smoke.rb +73 -0
- data/examples/server.rb +14 -0
- data/examples/smoke.rb +46 -0
- data/examples/symmetric_id_token_smoke.rb +83 -0
- data/lib/rodauth/ciba/authorization_details.rb +60 -0
- data/lib/rodauth/ciba/dpop.rb +250 -0
- data/lib/rodauth/ciba/http.rb +69 -0
- data/lib/rodauth/ciba/id_token_encryption.rb +175 -0
- data/lib/rodauth/ciba/jwks_cache.rb +149 -0
- data/lib/rodauth/ciba/mtls.rb +174 -0
- data/lib/rodauth/ciba/pairwise.rb +105 -0
- data/lib/rodauth/ciba/registration.rb +489 -0
- data/lib/rodauth/ciba/schema.rb +182 -0
- data/lib/rodauth/ciba/version.rb +6 -0
- data/lib/rodauth/ciba.rb +4 -0
- data/lib/rodauth/features/ciba.rb +2185 -0
- metadata +231 -0
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
# Dynamic CIBA registration: measured reference contract
|
|
2
|
+
|
|
3
|
+
DPoP client metadata now has a [separate measured contract](dpop-reference-contract.md#dynamic-registration-of-proof-required-clients): strict boolean validation when enabled, storage requirements, false/default serialization, issuance enforcement, full-replacement updates and ignored metadata when the feature is disabled. This does not establish nonce or all DPoP deployment combinations.
|
|
4
|
+
|
|
5
|
+
Reference: published `oidc-provider` 9.12.2, Node 24.21.0, complete HTTP OP
|
|
6
|
+
with its development memory adapter. The dedicated
|
|
7
|
+
[harness](../../test/reference/full-op/registration.mjs) and
|
|
8
|
+
[passing output](../validation/node-registration-reference.txt) establish these
|
|
9
|
+
observations, not a general DCR or pairwise conformance claim.
|
|
10
|
+
|
|
11
|
+
## Measured behavior
|
|
12
|
+
|
|
13
|
+
- Registration is discovered through `registration_endpoint`. This fixture
|
|
14
|
+
configures a fixed initial access token; an incorrect bearer token returns 401.
|
|
15
|
+
- A CIBA-only confidential poll client with `response_types: []` can omit both
|
|
16
|
+
`redirect_uris` and `client_name`. Registration returns 201, generated client
|
|
17
|
+
credentials, and an empty redirect URI array.
|
|
18
|
+
- Missing delivery mode, unsupported `push`, missing ping endpoint, an HTTP
|
|
19
|
+
ping endpoint, and a string instead of a user-code boolean return 400
|
|
20
|
+
`invalid_client_metadata`.
|
|
21
|
+
- Unknown internal-looking fields (`id`, `internal_admin`) are ignored; a
|
|
22
|
+
caller-supplied `client_secret` is replaced with a generated secret.
|
|
23
|
+
- A ping client with an HTTPS endpoint and boolean user-code metadata registers.
|
|
24
|
+
The test does not contact that endpoint or exercise user-code verification.
|
|
25
|
+
- A separate registered client combines ping, an RSA-signed request and required
|
|
26
|
+
user code. Its real signed request is rejected with the application's wrong
|
|
27
|
+
code and accepted with its approved code. This node fixture does not complete
|
|
28
|
+
that request or deliver its ping; those phases have separate harnesses.
|
|
29
|
+
- With RAR enabled, registration accepts a supported type-name array and rejects
|
|
30
|
+
a scalar, non-string elements, empty names and unknown types. A full update
|
|
31
|
+
omitting the type list returns an empty list and subsequent RAR requests are
|
|
32
|
+
rejected. The node fixture provides a resource indicator, as required for RAR.
|
|
33
|
+
- Pairwise plus client-secret-basic is rejected. Successful pairwise registration
|
|
34
|
+
and subject derivation are not established by this fixture.
|
|
35
|
+
- The generated poll-client credentials authenticate a real backchannel request.
|
|
36
|
+
After the application creates consent and completes the request, polling returns
|
|
37
|
+
access and ID tokens. This test checks issuance, not cryptographic verification
|
|
38
|
+
of those tokens (the lifecycle harness separately verifies signatures).
|
|
39
|
+
- With node's registrationManagement enabled, the issued registration bearer
|
|
40
|
+
reads its client and is echoed in the response. A partial update lacking
|
|
41
|
+
`client_id` is rejected; a full metadata update succeeds. A second replacement
|
|
42
|
+
omitting the name removes it and retains the original client secret, followed
|
|
43
|
+
by successful deletion. Default management updates rotate the bearer;
|
|
44
|
+
the previous bearer is then rejected with 401.
|
|
45
|
+
Presenting a bearer for a different client both returns 401 and invalidates
|
|
46
|
+
that bearer: a subsequent request for its own client also returns 401.
|
|
47
|
+
- Deleting a client with issued tokens and a pending CIBA request returns 204.
|
|
48
|
+
Subsequent poll returns 401 invalid_client and UserInfo returns 401
|
|
49
|
+
invalid_token. The node memory adapter still contains the Grant, AccessToken
|
|
50
|
+
and pending request records; record retention is distinct from usability.
|
|
51
|
+
|
|
52
|
+
The app callbacks for context and binding message are explicitly no-ops;
|
|
53
|
+
the composition fixture supplies a fixed user-code validation policy. The first attempted run reached
|
|
54
|
+
registration but failed request creation with the default context callback;
|
|
55
|
+
the corrected fixture now passes the complete registration-to-issuance path.
|
|
56
|
+
|
|
57
|
+
## Gem integration work still required
|
|
58
|
+
|
|
59
|
+
The gem now prepends `CibaSupport::Registration`, with
|
|
60
|
+
`ciba_dynamic_client_registration_enabled` defaulting to false. Initial opt-in
|
|
61
|
+
integration supports the measured public-subject poll registration and token
|
|
62
|
+
issuance path. Static-only behavior remains the default.
|
|
63
|
+
|
|
64
|
+
`test/ciba_registration_test.rb` covers generated credentials, real issuance,
|
|
65
|
+
invalid metadata without insertion, application registration authentication,
|
|
66
|
+
ordinary OAuth required fields, valid management updates, grant removal and
|
|
67
|
+
Discovery. A regression first reproduced a partial update to `subject_type`
|
|
68
|
+
being accepted for an existing CIBA client without resending `grant_types`.
|
|
69
|
+
The first repair merged stored metadata while holding the application row. The
|
|
70
|
+
current implementation instead uses node-style complete replacement for CIBA
|
|
71
|
+
management requests, retaining the row lock through validation/persistence
|
|
72
|
+
(SQLite uses an immediate transaction).
|
|
73
|
+
|
|
74
|
+
This is an initial integration, not a release-ready DCR claim. Additional
|
|
75
|
+
client-auth combinations and remaining metadata validation need coverage.
|
|
76
|
+
Full registration management response semantics
|
|
77
|
+
still need comparison with node. Package/Ruby-version coverage has not yet been
|
|
78
|
+
refreshed for this change.
|
|
79
|
+
|
|
80
|
+
## Registration security review and repairs
|
|
81
|
+
|
|
82
|
+
Regression tests reproduced upstream's fallback accepting arbitrary DB column
|
|
83
|
+
names and its management serializer exposing the registration access-token hash.
|
|
84
|
+
For CIBA registrations, the gem now selects explicit protocol metadata before
|
|
85
|
+
calling upstream validation. Unknown fields are ignored even if a matching DB
|
|
86
|
+
column exists; generated credentials replace caller-supplied create secrets.
|
|
87
|
+
This mirrors the measured node behavior for unknown fields and create secrets.
|
|
88
|
+
Custom metadata is not automatically admitted merely by adding a database column.
|
|
89
|
+
|
|
90
|
+
Management tests verify a wrong bearer cannot update the row and internal fields
|
|
91
|
+
cannot change identifiers/privilege/token hashes. The earlier compatibility
|
|
92
|
+
handling for ignored-fields-only partial updates has been superseded by full
|
|
93
|
+
replacement: a matching `client_id` is now mandatory, and server-issued token/URI
|
|
94
|
+
fields in the body are rejected before persistence.
|
|
95
|
+
CIBA management responses suppress stored credential hashes, include `client_id`,
|
|
96
|
+
and return scalar `token_endpoint_auth_method`. They now echo the authenticated
|
|
97
|
+
presented bearer and include the registration client URI, matching the measured
|
|
98
|
+
node GET behavior. The stored registration verifier is also suppressed on the
|
|
99
|
+
ordinary serializer path, so an older unprefixed client does not expose it after
|
|
100
|
+
removing its CIBA grant. Other ordinary response mapping remains upstream's.
|
|
101
|
+
|
|
102
|
+
## Initial management credential issuance
|
|
103
|
+
|
|
104
|
+
When CIBA DCR is enabled, `ciba_issue_registration_access_token` defaults to true.
|
|
105
|
+
Registration generates 32 random bytes encoded as a URL-safe bearer with a
|
|
106
|
+
`ciba_reg_` namespace prefix, stores only
|
|
107
|
+
its bcrypt verifier using upstream `password_hash`, and returns the raw bearer
|
|
108
|
+
and management URI inside the original registration transaction. It also stores
|
|
109
|
+
a unique SHA-256 lookup digest using `ciba_registration_token_digest_column`
|
|
110
|
+
(default `ciba_registration_token_digest`). The application table needs both
|
|
111
|
+
the upstream registration verifier column and this explicit additive migration:
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
Rodauth::CibaSupport::Schema.add_registration_token_digest(db)
|
|
115
|
+
# Custom table/column supported via table: and column: keyword arguments;
|
|
116
|
+
# configure ciba_registration_token_digest_column to match a custom column.
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Missing storage raises a configuration error rather than returning an unusable
|
|
120
|
+
management credential. Setting `ciba_issue_registration_access_token false`
|
|
121
|
+
omits issuance. The migration preserves existing rows with null digests. It does
|
|
122
|
+
not reconstruct digests from bcrypt verifiers: prefixed credentials issued by
|
|
123
|
+
the earlier development implementation need reissuance; unprefixed upstream
|
|
124
|
+
management credentials retain their previous authentication path.
|
|
125
|
+
|
|
126
|
+
The app must mount `load_registration_client_uri_routes` after `r.rodauth`, as
|
|
127
|
+
in the integration fixture. Mounting upstream's broad management route before
|
|
128
|
+
the registration POST route consumes that path and returns 404.
|
|
129
|
+
|
|
130
|
+
The gem test uses the issued credentials for GET, PUT and DELETE. Presenting a
|
|
131
|
+
new issued bearer to another client's URI now invalidates the presented bearer,
|
|
132
|
+
matching node; a subsequent request to the original client's URI returns 401.
|
|
133
|
+
The target client's row and the source's OAuth client secret remain unchanged.
|
|
134
|
+
Unknown or malformed bearers cannot revoke any client. Lookup and password-hash
|
|
135
|
+
verification are both required before a mismatch can invalidate credentials.
|
|
136
|
+
|
|
137
|
+
For ordinary management operations, the transaction locks the presented
|
|
138
|
+
credential's source row before validating the target client identifier. A mismatch clears only the source verifier/digest
|
|
139
|
+
and commits that deliberate invalidation before returning 401. It never locks
|
|
140
|
+
the unrelated target row, avoiding opposite cross-client requests locking each
|
|
141
|
+
other's rows. Concurrent updates of one client and opposite-client mismatches
|
|
142
|
+
are tested on separate connections; the latter invalidate only each presented
|
|
143
|
+
credential and leave OAuth secrets and an unrelated client unchanged.
|
|
144
|
+
Digest/verifier fields are omitted from management responses.
|
|
145
|
+
After invalidation, arbitrary unprefixed bearers are also rejected with 401:
|
|
146
|
+
the management-route-only hash matcher treats a missing verifier as a failed
|
|
147
|
+
match instead of passing nil to BCrypt and returning an internal error.
|
|
148
|
+
|
|
149
|
+
Deletion with existing CIBA and ordinary grants is now covered below. This work
|
|
150
|
+
does not establish full management parity.
|
|
151
|
+
|
|
152
|
+
## Full replacement updates
|
|
153
|
+
|
|
154
|
+
CIBA management PUT requires a matching `client_id`; mismatches or omission
|
|
155
|
+
return `invalid_request` without saving changes. Server-issued management token,
|
|
156
|
+
URI and issuance/secret-expiry fields are forbidden in the request. Omitted
|
|
157
|
+
protocol metadata is reset; explicit metadata is validated again. The client
|
|
158
|
+
identifier, client secret and app-owned internal columns are retained. The
|
|
159
|
+
registration credential is rotated by default as described below. Browser
|
|
160
|
+
response types require redirect URIs. Optional metadata's
|
|
161
|
+
complete default/type behavior still needs the feature-composition tests.
|
|
162
|
+
|
|
163
|
+
The `ciba_reg_` namespace selects this same management policy and safe response
|
|
164
|
+
serialization even after the client removes its CIBA grant. It is not an
|
|
165
|
+
authentication credential by itself: upstream still checks the full bearer
|
|
166
|
+
against the saved verifier before management validation or serialization.
|
|
167
|
+
Older unprefixed bearers rely on the client's current CIBA grant for selection
|
|
168
|
+
of the replacement policy; stored verifier redaction applies regardless.
|
|
169
|
+
|
|
170
|
+
Mounted-route coverage registers under `/identity/register` and verifies that
|
|
171
|
+
the same missing-client-id rejection applies there. Row-lock selection uses
|
|
172
|
+
the route's remaining path, matching upstream's routing rather than assuming
|
|
173
|
+
registration is mounted at the URL root.
|
|
174
|
+
|
|
175
|
+
## Optional-feature composition and storage
|
|
176
|
+
|
|
177
|
+
The gem test dynamically registers a client combining ping, RS256 request
|
|
178
|
+
signatures (separate client JWKS) and user-code validation. An unsigned request
|
|
179
|
+
and an incorrect code are rejected before request persistence. A correctly
|
|
180
|
+
signed request is completed, its ping dispatch goes through a test transport
|
|
181
|
+
with the registered endpoint/token, and polling returns a verified signed ID
|
|
182
|
+
Token for the newly generated client. This is not a real HTTPS delivery test;
|
|
183
|
+
the separate installed ping smoke supplies that transport evidence.
|
|
184
|
+
|
|
185
|
+
A full update then removes signing keys/algorithm and the notification endpoint,
|
|
186
|
+
switches to poll, and explicitly sets the user-code metadata false. The unsigned
|
|
187
|
+
request is now accepted under that new registration. This exposed and repaired
|
|
188
|
+
the upstream serializer dropping boolean false from the management response.
|
|
189
|
+
|
|
190
|
+
Another regression reproduced registration returning 201 with submitted JWKS
|
|
191
|
+
even though the application table lacked the JWKS column and upstream discarded
|
|
192
|
+
the key material. All accepted mapped CIBA-registration metadata must now have
|
|
193
|
+
storage before registration/update succeeds; missing columns return
|
|
194
|
+
`invalid_client_metadata` without inserting a client. Unknown ignored metadata
|
|
195
|
+
remains ignored. Column presence is not a claim that every optional feature,
|
|
196
|
+
key shape, algorithm or arbitrary schema type has been validated.
|
|
197
|
+
|
|
198
|
+
## RAR client metadata
|
|
199
|
+
|
|
200
|
+
With `ciba_authorization_details_enabled`, dynamic registration validates
|
|
201
|
+
`authorization_details_types` as an array of nonempty strings from the OP's
|
|
202
|
+
configured types. The array is serialized as JSON into the existing RAR client
|
|
203
|
+
column; create/read/update responses expose an array. Omission defaults to an
|
|
204
|
+
empty array, including full replacement updates. With the feature disabled,
|
|
205
|
+
the metadata is ignored and not persisted or echoed, even if that column exists.
|
|
206
|
+
|
|
207
|
+
The gem composition test registers a client for CIBA and refresh, requests RAR
|
|
208
|
+
read/delete actions for a resource, and saves approval for read only. Initial
|
|
209
|
+
resource token issuance and refresh carry only that approved read action. A
|
|
210
|
+
management update removing the registered type then prevents refresh of the
|
|
211
|
+
old RAR source. The fixed application callback defines action subset semantics;
|
|
212
|
+
this is not evidence of a generic RAR policy engine.
|
|
213
|
+
|
|
214
|
+
## Management token rotation
|
|
215
|
+
|
|
216
|
+
`ciba_rotate_registration_access_token` defaults to true, matching node's
|
|
217
|
+
`registrationManagement.rotateRegistrationAccessToken` default. A successful
|
|
218
|
+
PUT writes replacement metadata, a fresh verifier and its unique lookup digest
|
|
219
|
+
in one transaction, and responds with the new raw bearer. The old bearer is no
|
|
220
|
+
longer found; replay against either the original or another client cannot
|
|
221
|
+
invalidate the successor. GET does not rotate. Explicit false retains the bearer.
|
|
222
|
+
|
|
223
|
+
A response-serialization exception is injected after the new token is generated
|
|
224
|
+
and the update executes. The test confirms the row rolls back, the attempted
|
|
225
|
+
new bearer is rejected and the original bearer still authenticates. This follows
|
|
226
|
+
the accepted same-DB atomicity policy rather than claiming node's memory adapter
|
|
227
|
+
has equivalent failure behavior. Loss after a committed response is outside
|
|
228
|
+
that guarantee and requires application recovery/re-registration.
|
|
229
|
+
|
|
230
|
+
Two simultaneous PUTs using one bearer run on separate DB connections. Only one
|
|
231
|
+
can commit a new bearer. PostgreSQL/MySQL should reject the other with 401 after
|
|
232
|
+
acquiring the row lock. SQLite may instead time out waiting for its writer lock;
|
|
233
|
+
recognized BusyException (or adapter lock/serialization errors) returns 503
|
|
234
|
+
`temporarily_unavailable` with Retry-After. Retrying that request then returns
|
|
235
|
+
401. Other database errors are not mislabeled as retryable. This does not claim
|
|
236
|
+
crash recovery or guarantees for arbitrary application overrides.
|
|
237
|
+
|
|
238
|
+
## Deleting a client with live grants
|
|
239
|
+
|
|
240
|
+
The gem initially failed deletion when an ordinary OAuth grant referenced the
|
|
241
|
+
client: that grant has no CIBA-consent FK to cascade through. After authenticating
|
|
242
|
+
the management credential, CIBA-managed DELETE now removes OAuth grants belonging
|
|
243
|
+
to that client and the client itself within the existing outer transaction.
|
|
244
|
+
The supplied CIBA schema cascades consent, requests, refresh sources and private
|
|
245
|
+
ping delivery rows. Other clients and their grants remain unchanged. Legacy
|
|
246
|
+
unprefixed management credentials for a currently CIBA-enabled client use the
|
|
247
|
+
same cleanup after their own verifier check.
|
|
248
|
+
|
|
249
|
+
The test first proves opaque UserInfo and refresh work, then changes the client
|
|
250
|
+
to ping and creates a pending notification. It adds an ordinary grant to the
|
|
251
|
+
same client and an unrelated client's grant. A wrong bearer leaves all rows
|
|
252
|
+
unchanged. Successful deletion removes the target's credentials and dependent
|
|
253
|
+
records; UserInfo, refresh and pending poll are rejected. Ping data is pending,
|
|
254
|
+
so this test makes no outbound delivery request.
|
|
255
|
+
|
|
256
|
+
Unlike node's measured memory adapter, the SQL implementation removes dependent
|
|
257
|
+
rows using its established FK relationships; both measured OPs reject subsequent
|
|
258
|
+
use of the deleted client's opaque token and poll credentials. This does not
|
|
259
|
+
promise invalidation of externally validated self-contained JWTs before expiry.
|
|
260
|
+
|
|
261
|
+
A second test adds an application-owned restrictive FK. Failure to delete the
|
|
262
|
+
client rolls back the preceding grant cleanup, leaving its management credential
|
|
263
|
+
usable. The gem does not delete arbitrary application dependencies. The application
|
|
264
|
+
must resolve those dependencies or configure its own deletion policy. Custom
|
|
265
|
+
schema cascade policies remain unverified.
|
|
266
|
+
|
|
267
|
+
### Deletion lock order and refresh race
|
|
268
|
+
|
|
269
|
+
A deterministic PostgreSQL regression held a consent row while DELETE held the
|
|
270
|
+
client row, reproducing the reverse-order timeout. DELETE now verifies the
|
|
271
|
+
credential without a client lock, locks that client's existing consents in ID
|
|
272
|
+
order, then locks the client and revalidates the credential. A matching client
|
|
273
|
+
may therefore wait for a consent without blocking a refresh's parent-client FK
|
|
274
|
+
access. A mismatched URI still never locks the unrelated target's consents.
|
|
275
|
+
The controlled two-connection test passes on PostgreSQL/MySQL; SQLite's writer
|
|
276
|
+
serialization makes that row-lock-specific test inapplicable.
|
|
277
|
+
|
|
278
|
+
An actual concurrent DELETE/refresh test verifies eventual client deletion,
|
|
279
|
+
empty dependent tables and refusal of the old refresh token. Refresh may finish
|
|
280
|
+
before deletion or be rejected after it; recognized contention returns 503 and
|
|
281
|
+
deletion is retried. This exposed SQLite BusyException being reported as 500 by
|
|
282
|
+
the token endpoint: the same narrowly recognized condition now returns 503 there
|
|
283
|
+
as on management routes. Arbitrary DB errors still retain server-error handling.
|
|
284
|
+
These tests do not establish every interleaving with newly created consents,
|
|
285
|
+
ordinary OAuth issuance, external token consumers or custom transaction hooks.
|
|
286
|
+
|
|
287
|
+
Installed rodauth-oauth 1.7.0 requires `redirect_uris` and `client_name` before
|
|
288
|
+
calling `validate_client_registration_params`. Its registration implementation
|
|
289
|
+
also defaults omitted grants and responses to authorization_code/code. Therefore
|
|
290
|
+
removing the static rejection alone cannot implement the measured contract.
|
|
291
|
+
|
|
292
|
+
The complete integration must cover:
|
|
293
|
+
|
|
294
|
+
1. Explicit opt-in and CIBA-specific required/default metadata without changing
|
|
295
|
+
unrelated OAuth registration behavior.
|
|
296
|
+
2. Validated delivery mode, notification endpoint, user-code boolean, request
|
|
297
|
+
signing algorithm/key requirements, and supported subject/authentication
|
|
298
|
+
combinations before persistence. Optional capability flags and migrations
|
|
299
|
+
must constrain the accepted metadata.
|
|
300
|
+
3. Real register/start/complete/poll behavior with generated credentials, plus
|
|
301
|
+
rejection cases that leave no newly registered client.
|
|
302
|
+
4. Registration management updates: changing grant types, CIBA metadata or
|
|
303
|
+
authentication must not bypass the same checks. Authorization remains the
|
|
304
|
+
registration feature/application's responsibility.
|
|
305
|
+
|
|
306
|
+
Pairwise remains a separate incomplete boundary; rejecting unsupported pairwise
|
|
307
|
+
registration initially is not full alignment with the reference.
|
|
308
|
+
|
|
309
|
+
## Reproduction
|
|
310
|
+
|
|
311
|
+
From `test/reference/full-op`, after installing the pinned dependencies:
|
|
312
|
+
|
|
313
|
+
```sh
|
|
314
|
+
docker run --rm --network none \
|
|
315
|
+
--mount "type=bind,src=$PWD,dst=/app,readonly" --workdir /app \
|
|
316
|
+
node:24-bookworm-slim@sha256:0e0ff40c39bc087845bfb27465a0df4ea419520094bc35842ff83dd8cbe6f9b6 \
|
|
317
|
+
node registration.mjs
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Only loopback listeners are used; no external network or production credentials.
|
|
321
|
+
Source inspection: `lib/helpers/client_schema.js` and
|
|
322
|
+
`lib/actions/registration.js` in the pinned node package, and upstream
|
|
323
|
+
`lib/rodauth/features/oauth_dynamic_client_registration.rb`.
|
|
324
|
+
|
|
325
|
+
### Dynamically registered private_key_jwt
|
|
326
|
+
|
|
327
|
+
The real node OP accepts a dynamically registered RSA public JWKS for CIBA
|
|
328
|
+
client authentication. Wrong key, wrong audience and replayed assertion are
|
|
329
|
+
rejected. An issuer audience is accepted at both the backchannel and CIBA token
|
|
330
|
+
endpoints. The gem previously accepted issuer only at the backchannel endpoint;
|
|
331
|
+
its CIBA grant token path now also accepts issuer, retaining endpoint audiences.
|
|
332
|
+
Other token grant paths and unrelated clients retain their existing policy.
|
|
333
|
+
|
|
334
|
+
The gem integration also replaces the registered JWKS using authenticated
|
|
335
|
+
management PUT: fresh assertions signed with the removed key are rejected and
|
|
336
|
+
the replacement key works. This is inline JWKS replacement; remote JWKS cache
|
|
337
|
+
invalidation and algorithm metadata validation are separate remaining work.
|
|
338
|
+
|
|
339
|
+
### Client authentication signing algorithm
|
|
340
|
+
|
|
341
|
+
`token_endpoint_auth_signing_alg` is optional. When supplied, CIBA registration
|
|
342
|
+
checks its string type, the registered JWT authentication method and the OP's
|
|
343
|
+
advertised algorithms. `none`, an asymmetric/HMAC method mismatch, unsupported
|
|
344
|
+
algorithms and use with Basic authentication are rejected before insertion.
|
|
345
|
+
The application must add a nullable String `token_endpoint_auth_signing_alg`
|
|
346
|
+
column, or configure `oauth_applications_token_endpoint_auth_signing_alg_column`
|
|
347
|
+
to its mapped column; requests supplying the field fail if storage is absent.
|
|
348
|
+
Omission preserves the previous assertion algorithm policy.
|
|
349
|
+
|
|
350
|
+
Authentication compares the assertion header with the saved algorithm before
|
|
351
|
+
signature verification. For private_key_jwt the selected algorithm is also
|
|
352
|
+
passed to the upstream decoder. A regression using keys for RS256 and RS512
|
|
353
|
+
proved that previously an RS512-registered client could authenticate with RS256;
|
|
354
|
+
it now rejects RS256 and accepts RS512. The same registered constraint applies
|
|
355
|
+
on CIBA-capable clients' existing protected assertion authentication paths.
|
|
356
|
+
|
|
357
|
+
The real node fixture explicitly enables RS256/RS512 through
|
|
358
|
+
`enabledJWA.clientAuthSigningAlgValues`; RS512 is not a node default. Gem tests
|
|
359
|
+
configure these through upstream `oauth_jwt_keys`, which supplies its existing
|
|
360
|
+
Discovery signing-algorithm list. This does not decouple client-auth algorithms
|
|
361
|
+
from OP signing configuration. HMAC and other algorithm-family combinations
|
|
362
|
+
need further comparative coverage; the RSA test is not proof of all families.
|
|
363
|
+
|
|
364
|
+
### Inline JWKS registration validation
|
|
365
|
+
|
|
366
|
+
Inline `jwks` must contain a `keys` array of objects. Recognized RSA, EC, OKP
|
|
367
|
+
and AKP entries have the reference provider's structural checks for required
|
|
368
|
+
public fields, forbidden private parameters, optional alg/kid/use strings and
|
|
369
|
+
x5c arrays. Symmetric oct entries are rejected. Like node, unknown key types
|
|
370
|
+
and unsupported curves are not required to pass recognized-key checks; neither
|
|
371
|
+
registration acceptance nor an empty set proves usable authentication keys.
|
|
372
|
+
Actual signature verification remains necessary. This validation applies to
|
|
373
|
+
CIBA registration and management replacement, without changing ordinary OAuth
|
|
374
|
+
registration's upstream policy.
|
|
375
|
+
|
|
376
|
+
The real node and Ruby fixtures cover malformed top-level/entry structure,
|
|
377
|
+
symmetric keys, every forbidden RSA private parameter, malformed RSA public
|
|
378
|
+
fields and metadata, plus acceptance of an empty set. The gem also verifies
|
|
379
|
+
that rejected management replacement leaves both metadata and the current
|
|
380
|
+
management credential intact. These fixtures do not establish operational
|
|
381
|
+
support for every curve or key type, or validate remote JWKS responses.
|
|
382
|
+
|
|
383
|
+
### Remote assertion keys and management URL replacement
|
|
384
|
+
|
|
385
|
+
A real loopback HTTP fixture exercises dynamic private_key_jwt registration,
|
|
386
|
+
JWT authentication, an upstream max-age cache hit, and authenticated management
|
|
387
|
+
PUT replacing jwks_uri. Registration's URI policy is explicitly relaxed only
|
|
388
|
+
for the two fixture URLs. With the normal outbound address policy, the first
|
|
389
|
+
assertion must fail without contacting loopback. After a test-only address
|
|
390
|
+
allowance, the old URI is fetched once and cached; changing to the new URI
|
|
391
|
+
rejects old-key assertions and accepts the new key, without refetching the old
|
|
392
|
+
URI. Malformed JSON and oversized responses fail authentication without saving
|
|
393
|
+
pending requests.
|
|
394
|
+
|
|
395
|
+
This exposed private_key_jwt using upstream HTTP directly, bypassing the
|
|
396
|
+
outbound controls already used for signed CIBA authentication requests. The
|
|
397
|
+
protected client-assertion lookup now uses the same bounded, address-checked
|
|
398
|
+
transport. Fetch/parse failures become invalid_client. Unrelated client flows
|
|
399
|
+
retain upstream transport behavior.
|
|
400
|
+
|
|
401
|
+
The published node OP is now exercised by `registration-remote.mjs` over real
|
|
402
|
+
HTTP with its memory adapter. It rejects default loopback lookup with 401
|
|
403
|
+
invalid_client without reaching the receiver. A test-only exception for the
|
|
404
|
+
two owned URLs then permits one fetch and cache reuse. Changing the served key
|
|
405
|
+
at the same URL while max-age=3600 is fresh rejects the new kid, still accepts
|
|
406
|
+
the cached old key and performs no fetch. Management PUT changing jwks_uri then
|
|
407
|
+
rejects the old key and accepts the new key after one new-URL fetch. The gem's
|
|
408
|
+
matching test confirms this sequence; no unknown-kid refresh was added.
|
|
409
|
+
|
|
410
|
+
[Node log](../validation/node-registration-remote-reference.txt),
|
|
411
|
+
[gem DB comparison](../validation/registration-remote-cache.txt).
|
|
412
|
+
This establishes the measured fresh-cache and URL-replacement behavior only.
|
|
413
|
+
Node's source applies a minimum 60-second freshness interval, whereas upstream
|
|
414
|
+
Ruby follows its own HTTP cache policy; no-cache/expiry timing, same-URL metadata
|
|
415
|
+
updates, shared cache ownership and cross-process invalidation still require
|
|
416
|
+
separate comparison. Do not infer universal cache equivalence.
|
|
417
|
+
|
|
418
|
+
## default_max_age validation and persistence
|
|
419
|
+
|
|
420
|
+
The reference OP accepts non-negative safe integer values (0 through
|
|
421
|
+
9007199254740991), and rejects negative/fractional numbers, strings, booleans,
|
|
422
|
+
arrays, objects and larger numbers. The gem previously returned 201 for -1;
|
|
423
|
+
valid values were echoed but dropped from insertion because upstream's generic
|
|
424
|
+
metadata key did not map to a symbol column. Both boundaries are now explicit.
|
|
425
|
+
Integral JSON numbers such as 1.0 normalize to an integer before storage.
|
|
426
|
+
|
|
427
|
+
To accept this optional metadata, add a nullable BIGINT `default_max_age` column
|
|
428
|
+
to the application's client table. Override
|
|
429
|
+
`oauth_applications_default_max_age_column` if using another column name.
|
|
430
|
+
Without storage, supplying this metadata is rejected. Failed management PUT
|
|
431
|
+
preserves the whole client row, including its management credential; authenticated
|
|
432
|
+
GET returns the stored value, including zero.
|
|
433
|
+
|
|
434
|
+
This is registration metadata support, not implementation of browser OIDC
|
|
435
|
+
reauthentication or a new CIBA parameter. The application still implements
|
|
436
|
+
authentication policy; no claim of automatic freshness enforcement follows.
|
|
437
|
+
[Reference HTTP results](../validation/node-registration-max-age.txt),
|
|
438
|
+
[initial failing test](../validation/registration-max-age-red.txt).
|
|
439
|
+
|
|
440
|
+
The full three-DB suite passes **265 tests** before the final 1.0 normalization;
|
|
441
|
+
the final targeted test passes **59 assertions** on each DB, including missing
|
|
442
|
+
storage, bad values, persisted valid values, management rollback and GET.
|
|
443
|
+
[Full DB log](../validation/registration-max-age-matrix.txt),
|
|
444
|
+
[final change](../validation/registration-max-age-final.txt).
|
|
445
|
+
|
|
446
|
+
## require_auth_time boolean metadata
|
|
447
|
+
|
|
448
|
+
The pinned reference accepts only JSON booleans for `require_auth_time` and
|
|
449
|
+
returns both true and false from registration and authenticated management GET.
|
|
450
|
+
The gem previously accepted the string `"true"` and echoed it in a201 response.
|
|
451
|
+
CIBA registration now rejects non-boolean values before persistence and maps the
|
|
452
|
+
metadata to an explicit column. Its management serializer also preserves false;
|
|
453
|
+
upstream's generic serializer omits false-valued columns.
|
|
454
|
+
|
|
455
|
+
Applications accepting this metadata must add a nullable boolean
|
|
456
|
+
`require_auth_time` column, or override
|
|
457
|
+
`oauth_applications_require_auth_time_column`. Supplying it without storage is
|
|
458
|
+
rejected. Invalid management replacement leaves the client row and management
|
|
459
|
+
credential unchanged. No new table requirement applies when it is omitted.
|
|
460
|
+
This registration change validates and stores metadata. The separate token-output
|
|
461
|
+
comparison and subsequent implementation are recorded below.
|
|
462
|
+
|
|
463
|
+
Evidence: [reference registration and GET](../validation/node-registration-auth-time.txt),
|
|
464
|
+
[pre-fix failure](../validation/registration-auth-time-red.txt), and
|
|
465
|
+
[Ruby3.4 SQLite/PostgreSQL/MySQL](../validation/registration-auth-time-matrix.txt),
|
|
466
|
+
2 tests /95 assertions on each backend including default_max_age regression.
|
|
467
|
+
The DB run uses existing test-image dependencies with current lib/test/examples
|
|
468
|
+
mounted read-only; it is not a new image build or a full-suite run.
|
|
469
|
+
|
|
470
|
+
### Measured auth_time output difference
|
|
471
|
+
|
|
472
|
+
The dedicated `auth-time.mjs` reference fixture checks six configurations:
|
|
473
|
+
require_auth_time omitted/false/true, each with completion authTime omitted/123.
|
|
474
|
+
All initial and refreshed ID Tokens are independently signature-verified.
|
|
475
|
+
|
|
476
|
+
| Completion time | require_auth_time | Node initial/refresh | Current gem initial/refresh |
|
|
477
|
+
|---|---|---|---|
|
|
478
|
+
| omitted | omitted, false or true | claim absent | claim absent |
|
|
479
|
+
| 123 | omitted or false | claim absent | auth_time=123 |
|
|
480
|
+
| 123 | true | auth_time=123 | auth_time=123 |
|
|
481
|
+
|
|
482
|
+
[Reference execution](../validation/node-auth-time.txt),
|
|
483
|
+
[gem characterization:1 test /48 assertions](../validation/auth-time-current.txt).
|
|
484
|
+
The gem test records current behavior, not an alignment pass. Neither implementation
|
|
485
|
+
invents a timestamp when the application omits it, even for require_auth_time=true.
|
|
486
|
+
Node's common grant helper copies source.authTime to available claims; its claim
|
|
487
|
+
selection, including authorization/claims.js, controls output. The existing gem
|
|
488
|
+
id_token_claims emits any supplied context directly. Before changing it, reconcile
|
|
489
|
+
explicit claims requests, scope-selected authentication claims and initial/refresh
|
|
490
|
+
selection together; do not merely discard the persisted authentication context.
|
|
491
|
+
This was an implementation gap, not an accepted adaptation. The subsequent
|
|
492
|
+
[authentication claim selection](../authentication-claims.md) implementation now
|
|
493
|
+
saves request selection and applies it on initial issuance and refresh. NULL legacy
|
|
494
|
+
sources retain their old output contract as a migration boundary.
|
|
495
|
+
|
|
496
|
+
### Authentication selection correction evidence
|
|
497
|
+
|
|
498
|
+
The extended reference tests cover18 scenarios each with default and explicit
|
|
499
|
+
scope mappings ([default](../validation/node-authentication-claims.txt),
|
|
500
|
+
[scope mapping](../validation/node-authentication-claims-scope.txt)); each verifies
|
|
501
|
+
both signed initial and refreshed ID Tokens. ACR/AMR support is explicitly configured.
|
|
502
|
+
The gem uses stored request selection plus an application scope mapping, without
|
|
503
|
+
changing stored auth_time/acr/amr values or permitting account claims to replace them.
|
|
504
|
+
[Ruby3.4 three-DB tests](../validation/authentication-claims-matrix.txt) pass13 tests
|
|
505
|
+
/951 assertions each, including saved selection across metadata changes, legacy
|
|
506
|
+
migration and existing pairwise/Edwards/RAR refresh cases. The earlier
|
|
507
|
+
auth-time-current.txt is a pre-correction characterization log.
|
|
508
|
+
|
|
509
|
+
## default_acr_values and authentication defaults
|
|
510
|
+
|
|
511
|
+
The pinned reference accepts arrays of configured ACR strings, rejects other
|
|
512
|
+
values and unsupported strings, and removes duplicates while preserving first
|
|
513
|
+
occurrence order. Empty arrays are valid. Registration and management GET retain
|
|
514
|
+
the normalized array ([reference](../validation/node-registration-acr-defaults.txt)).
|
|
515
|
+
|
|
516
|
+
CIBA registration now follows that contract. Applications accepting this metadata
|
|
517
|
+
add a nullable text `default_acr_values` column (JSON array), or override
|
|
518
|
+
`oauth_applications_default_acr_values_column`, and configure upstream
|
|
519
|
+
`oauth_acr_values_supported`. Missing storage rejects the metadata; invalid
|
|
520
|
+
management replacement preserves the whole client and credential. The metadata
|
|
521
|
+
column does not configure which ACRs the OP supports.
|
|
522
|
+
|
|
523
|
+
At request acceptance, omitted acr_values uses the client's defaults in order;
|
|
524
|
+
explicit acr_values takes precedence. The selected ACR is exposed to the device
|
|
525
|
+
callback through the ordinary request snapshot. An empty default list makes no
|
|
526
|
+
selection. Completion still supplies the achieved ACR, which is not inferred
|
|
527
|
+
from the requested values. Selection survives later client metadata changes and
|
|
528
|
+
refresh. Static registrations store the same JSON array representation.
|
|
529
|
+
|
|
530
|
+
The reference also selects auth_time when default_max_age is configured, including
|
|
531
|
+
zero; the gem now does likewise. In the reference test an old supplied authTime
|
|
532
|
+
still produces a token. Neither this output selection nor metadata persistence
|
|
533
|
+
proves freshness enforcement. The reference retains max_age in request.params;
|
|
534
|
+
the gem subsequently added a saved maximum-age input in its device request
|
|
535
|
+
snapshot. See [maximum authentication age](../max-age.md) for request precedence,
|
|
536
|
+
normalized zero semantics and migration. Freshness enforcement remains application-owned.
|
|
537
|
+
|
|
538
|
+
[Reference defaults fixture](../validation/node-authentication-defaults.txt)
|
|
539
|
+
verifies18 initial/refresh configurations, including explicit ACR precedence.
|
|
540
|
+
[Local tests](../validation/authentication-defaults-current.txt) pass2 tests /147
|
|
541
|
+
assertions, including registration rollback, preference order and metadata-change
|
|
542
|
+
isolation. Empty request acr_values initially remained stricter; the subsequent correction
|
|
543
|
+
below treats an empty string like omission.
|
|
544
|
+
|
|
545
|
+
Final related DB validation: [Ruby3.4 matrix](../validation/authentication-defaults-matrix.txt)
|
|
546
|
+
passes17 tests /1,193 assertions on SQLite, PostgreSQL and MySQL, with no
|
|
547
|
+
failures/errors/skips. This is the focused defaults/registration/authentication
|
|
548
|
+
selection subset; the earlier385-test full run predates these defaults changes.
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
### Empty ACR request correction
|
|
552
|
+
|
|
553
|
+
The gem now treats acr_values="" like omission, matching the pinned reference's
|
|
554
|
+
request/default assignment. Registered ordered defaults apply when present;
|
|
555
|
+
without defaults, empty input does not select an ACR claim. An explicit nonempty
|
|
556
|
+
value still overrides the defaults. No whitespace trimming or changed validation
|
|
557
|
+
of other parameters is implied.
|
|
558
|
+
|
|
559
|
+
Reference evidence covers24 initial/refresh cases each for
|
|
560
|
+
[ordinary requests without defaults](../validation/node-empty-acr-default.txt)
|
|
561
|
+
and [signed requests with defaults](../validation/node-empty-acr-signed.txt).
|
|
562
|
+
The signed fixture supplies a conflicting unsigned outer ACR; it never replaces
|
|
563
|
+
the signed empty/omitted value or the registered fallback. The gem regression
|
|
564
|
+
covers omitted/empty/explicit input with nil/empty/ordered defaults in both modes.
|
|
565
|
+
[Pre-fix rejection](../validation/empty-acr-red.txt),
|
|
566
|
+
[final related tests](../validation/empty-acr-final.txt). The intermediate
|
|
567
|
+
empty-acr-current.txt has local socket permission errors and is not a passing run.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Authentication request validation inventory
|
|
2
|
+
|
|
3
|
+
This reconciles CIBA-067 against the implemented request parser. It records
|
|
4
|
+
protocol validation, not identical policy defaults in every node deployment or
|
|
5
|
+
an independent conformance certification. Required presence and exclusive hint
|
|
6
|
+
selection are separately covered by the [mandatory inventory](required-parameter-inventory.md).
|
|
7
|
+
|
|
8
|
+
All test references below are in `test/`; each named test is included in the
|
|
9
|
+
[current400-test run](../validation/id-hint-validation-full-ruby33.txt).
|
|
10
|
+
|
|
11
|
+
| Inputs | Implemented boundary | Direct tests |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Form and repeated parameters | TLS POST/form, no query parameters, bounded body, encoding and duplicate checks; repeated resource has its defined handling | `ciba_protocol_test.rb:test_ciba_endpoints_require_tls_post_and_form_before_state_changes`, `test_duplicate_parameters_are_rejected`; `ciba_resources_test.rb:test_resource_validation_and_additive_migration` |
|
|
14
|
+
| scope | Required openid, scope syntax and registered permissions; offline_access obeys refresh activation | `ciba_requirements_test.rb:test_scope_syntax_errors_are_invalid_scope`; `ciba_protocol_test.rb:test_expiry_binding_context_and_offline_access` |
|
|
15
|
+
| login_hint / login_hint_token | Exclusive enabled hint, string/size bounds; application resolution and eligible account | `ciba_protocol_test.rb:test_request_validation_and_unknown_parameters`; `ciba_hint_token_test.rb:test_hint_token_input_exclusivity_size_and_unknown_user` |
|
|
16
|
+
| id_token_hint | Signature, configured algorithm, issuer/audience/subject, shape/time checks and account mapping; expired tokens are identity hints | `ciba_id_token_hint_test.rb:test_id_hint_rejects_untrusted_claims_and_signatures_before_subject_resolution`, `test_id_hint_malformed_signed_claims_are_protocol_errors`, `test_id_hint_expired_token_is_only_an_identity_hint` |
|
|
17
|
+
| binding_message / requested_expiry | Application binding-message validator, positive integer expiry and configured cap | `ciba_protocol_test.rb:test_expiry_binding_context_and_offline_access`; `ciba_requirements_test.rb:test_requested_expiry_expires_at_the_exact_boundary` |
|
|
18
|
+
| acr_values | Bounded string, empty/omitted registered defaults; authentication policy remains application-owned | `ciba_authentication_claims_test.rb:test_empty_acr_values_uses_defaults_in_plain_and_signed_requests`, `test_registered_authentication_defaults_apply_before_dispatch` |
|
|
19
|
+
| request / request_uri | Registered signed JWT, key/signature/required claims, inner parameters replace outer; nested/remote request references rejected | `ciba_signed_request_test.rb:test_signed_request_rejects_bad_claims_and_keys_before_persistence`, `test_signed_request_missing_required_fields_cannot_use_outer_values` |
|
|
20
|
+
| user_code | Feature activation, bounded string and mandatory application verifier, before persistence | `ciba_user_code_test.rb:test_user_code_input_and_optional_client_policy`, `test_user_code_requires_explicit_callback_and_checks_before_device_dispatch` |
|
|
21
|
+
| client_notification_token | Required for ping; bearer syntax and size | `ciba_ping_test.rb:test_ping_notification_token_bearer_syntax_and_maximum_length`, `test_signed_ping_required_fields_cannot_be_supplied_outside_signature` |
|
|
22
|
+
| nonce | Optional bounded string, bound to accepted request rather than poll | `ciba_nonce_test.rb:test_nonce_is_optional_and_bounded`, `test_nonce_is_bound_to_start_request_not_poll_and_not_observed` |
|
|
23
|
+
| claims | Enabled JSON object/target shape, separate saved consent; claim values are not identity data | `ciba_claims_test.rb:test_claim_input_validation_and_disabled_consent`, `test_claim_rejection_overrides_scope_and_request_value_is_not_identity_data` |
|
|
24
|
+
| resource | Enabled registered URI targets, duplicate handling, scope and audience separation | `ciba_resources_test.rb:test_resource_validation_and_additive_migration`, `test_resource_scope_and_audience_match_reference_and_id_token_keeps_oidc_context` |
|
|
25
|
+
| authorization_details | Enabled JSON structure, registered types, target and application policy | `ciba_rar_test.rb:test_rar_request_shape_client_types_and_resource_are_validated` |
|
|
26
|
+
| max_age | Bounded numeric normalization, safe integer range and defaults; authentication freshness is enforced by the application | `ciba_max_age_test.rb:test_max_age_invalid_values_do_not_dispatch_or_persist` |
|
|
27
|
+
| request_context | Enabled bounded string and mandatory application validator before dispatch; signed inner value isolation | `ciba_request_context_test.rb:test_request_context_rejection_precedes_persistence_and_device_dispatch`, `test_request_context_signed_input_replaces_outer_context` |
|
|
28
|
+
| Unknown input | Ignored; cannot write server-side state | `ciba_protocol_test.rb:test_login_hint_is_application_defined_and_unknown_fields_cannot_set_state` |
|
|
29
|
+
|
|
30
|
+
Client authentication is processed independently before these request values.
|
|
31
|
+
Content-specific interpretation of opaque hints, authentication strength,
|
|
32
|
+
claims, resources, rich authorization details and request context belongs to
|
|
33
|
+
the configured application callbacks; parser validation does not establish
|
|
34
|
+
production policy correctness. Disabled optional features and unknown fields
|
|
35
|
+
follow their documented handling rather than implicitly enabling a feature.
|
|
36
|
+
|
|
37
|
+
Remaining compatibility choices are explicit: user-code activation (CIBA-013),
|
|
38
|
+
hint retention (CIBA-151). The previous stricter ID-hint profile has now been
|
|
39
|
+
aligned as documented in [the hint policy](../id-token-hint.md), with a three-DB regression and the updated400-test full run above. They are not hidden missing parser
|
|
40
|
+
features, nor are they declared approved by this inventory. Remote key cache
|
|
41
|
+
policy is also still pending. A full Cartesian product of extensions is not
|
|
42
|
+
necessary evidence for this aggregate parameter-validation row.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Required CIBA authentication parameters
|
|
2
|
+
|
|
3
|
+
This inventory reconciles CIBA-072 with the enabled implementation. It concerns
|
|
4
|
+
mandatory presence and selection, not a blanket pass for all optional parameter
|
|
5
|
+
values (CIBA-067), application policy, or every combination of extensions.
|
|
6
|
+
|
|
7
|
+
| Condition | Required input | Direct test evidence |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Every authentication request | `scope`, containing `openid` | `ciba_requirements_test.rb:test_scope_syntax_errors_are_invalid_scope`; `ciba_protocol_test.rb:test_request_validation_and_unknown_parameters` |
|
|
10
|
+
| Every authentication request | Exactly one enabled hint | `ciba_hint_token_test.rb:test_hint_token_input_exclusivity_size_and_unknown_user`; `ciba_protocol_test.rb:test_request_validation_and_unknown_parameters` |
|
|
11
|
+
| Client registered for signed requests | `request` JWT | `ciba_signed_request_test.rb:test_signed_request_registration_discovery_and_client_authentication` |
|
|
12
|
+
| Signed request | `iss`, `aud`, `exp`, `iat`, `nbf`, `jti`, and inner scope/hint | `ciba_signed_request_test.rb:test_signed_request_missing_required_fields_cannot_use_outer_values`; `test_signed_request_rejects_bad_claims_and_keys_before_persistence` |
|
|
13
|
+
| Ping delivery | `client_notification_token` | `ciba_ping_test.rb:test_ping_rejects_bad_tokens_and_uses_current_registered_destination` |
|
|
14
|
+
| User-code verifier requires a code | `user_code` | `ciba_user_code_test.rb:test_user_code_requires_explicit_callback_and_checks_before_device_dispatch` |
|
|
15
|
+
| Signed request with ping and required user code | Required authentication fields must be inside the signature | `ciba_ping_test.rb:test_signed_ping_required_fields_cannot_be_supplied_outside_signature` |
|
|
16
|
+
|
|
17
|
+
Client authentication is independent and precedes request-object processing.
|
|
18
|
+
The signed-request registration test rejects a valid JWT without client
|
|
19
|
+
authentication; the existing authentication audit covers the registered
|
|
20
|
+
credential mechanisms. A JWT does not supply client authentication merely by
|
|
21
|
+
containing `iss` or `client_id`.
|
|
22
|
+
|
|
23
|
+
`binding_message`, `requested_expiry`, `acr_values`, nonce, claims, resources,
|
|
24
|
+
authorization_details, max_age and request_context are optional protocol inputs.
|
|
25
|
+
Their application policies or content validation must not be confused with an
|
|
26
|
+
unconditional presence requirement. A configured application may impose further
|
|
27
|
+
policy through the documented callbacks. Push remains unsupported in both the
|
|
28
|
+
comparison target and gem.
|
|
29
|
+
|
|
30
|
+
The route validates transport and client authentication, processes the signed
|
|
31
|
+
request, validates scope/hints, validates ping parameters, resolves the account
|
|
32
|
+
and invokes application verifiers before inserting a pending request. The
|
|
33
|
+
signed omission and combined-feature regressions assert absence of persistence
|
|
34
|
+
and device dispatch on rejection, with successful pending controls.
|
|
35
|
+
|
|
36
|
+
[Current full Ruby 3.3 execution](../validation/alignment-consolidated-ruby33.txt):
|
|
37
|
+
395 tests, 10,593 assertions, zero failures/errors, two SQLite row-lock skips.
|
|
38
|
+
[Focused omission execution](../validation/signed-required-omissions.txt):
|
|
39
|
+
3 tests, 119 assertions, no failures/errors/skips. Test files are under `test/`.
|