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,288 @@
|
|
|
1
|
+
# CIBA ID Token encryption
|
|
2
|
+
|
|
3
|
+
> Latest policy supersedes the historical minimum-floor notes below: use explicit
|
|
4
|
+
> HTTP freshness, and permit failed-fetch reuse only with publisher stale-if-error,
|
|
5
|
+
> capped at60 seconds without renewing deadlines. See [operations](operations.md#remote-jwks-cache-lifetime).
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
> Cache policy update: the user approved a minimum60-second successful JWKS
|
|
9
|
+
> lifetime shared by CIBA authentication and encryption, without a new policy
|
|
10
|
+
> switch. Earlier shorter-TTL comparisons below describe the previous runtime.
|
|
11
|
+
> Failed-refresh reuse remains excluded; expiry followed by failed retrieval
|
|
12
|
+
> does not renew freshness. See the current alignment closeout and Jev review.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
Enable recipient encryption explicitly:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
ciba_id_token_encryption_enabled true
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For RSA encryption, register the client's public `jwks` or HTTPS `jwks_uri`. Select
|
|
22
|
+
`id_token_encrypted_response_alg` plus `id_token_encrypted_response_enc`.
|
|
23
|
+
Create storage columns for those two strings and the selected key metadata.
|
|
24
|
+
Dynamic registration defaults omitted `enc` to `A128CBC-HS256` when `alg` is
|
|
25
|
+
present; `enc` without `alg` is rejected. Discovery publishes enabled ID Token
|
|
26
|
+
encryption algorithms without widening UserInfo/request-object encryption lists.
|
|
27
|
+
The default flag is false. Configured encryption must never fall back to a signed
|
|
28
|
+
plaintext ID Token; a failure rolls back issuance and leaves approval available.
|
|
29
|
+
|
|
30
|
+
With the capability disabled, dynamic registration treats the two encryption
|
|
31
|
+
fields as unsupported metadata: they are ignored, omitted from its response,
|
|
32
|
+
and not stored. This follows the measured reference behavior. Clients must
|
|
33
|
+
inspect returned registration metadata. This does not erase a statically
|
|
34
|
+
configured encryption requirement or silently bypass one during issuance.
|
|
35
|
+
|
|
36
|
+
The implementation supports RSA-OAEP and RSA-OAEP-256 recipient keys, plus
|
|
37
|
+
client-secret-derived `dir`, A128KW/A192KW/A256KW and
|
|
38
|
+
A128GCMKW/A192GCMKW/A256GCMKW, and ECDH-ES with optional AES-KW. These use A128GCM/A192GCM/A256GCM and A128CBC-HS256/A192CBC-HS384/A256CBC-HS512 content
|
|
39
|
+
encryption. It first signs the ID Token, then encrypts it using `jwe`1.1.1
|
|
40
|
+
and the CIBA-local GCMKW/ECDH adapters.
|
|
41
|
+
RSA and Edwards ID Token signing both use this path, including refresh.
|
|
42
|
+
The outer protected header carries cty=JWT, selected kid (if present), issuer
|
|
43
|
+
and audience; no compression is enabled.
|
|
44
|
+
|
|
45
|
+
For RSA algorithms, only registered client RSA keys of at least 2048 bits qualify. Optional alg must
|
|
46
|
+
match; optional use must be enc. If key_ops is declared, selection requires
|
|
47
|
+
encrypt and wrapping requires wrapKey. Both operations must be present to issue.
|
|
48
|
+
A missing metadata-compatible recipient returns400 invalid_client_metadata;
|
|
49
|
+
selected unusable key material or operation declarations fail processing. None
|
|
50
|
+
of these failures falls back to an OP key. Existing JWKS retrieval controls
|
|
51
|
+
apply. Do not supply a client's private key to the OP.
|
|
52
|
+
|
|
53
|
+
When several keys qualify, keys explicitly matching alg and use have higher
|
|
54
|
+
priority than keys omitting those fields. Equal scores preserve their input
|
|
55
|
+
order, matching node-oidc-provider. Actual reference issuance and the gem's
|
|
56
|
+
regression put a generic key first and verify that the explicitly tagged second
|
|
57
|
+
key receives the encrypted token. [Before correction](validation/id-token-recipient-priority-red.txt),
|
|
58
|
+
[three-DB regression](validation/id-token-recipient-priority-matrix.txt),
|
|
59
|
+
[reference](validation/node-encrypted-id-tokens.txt).
|
|
60
|
+
|
|
61
|
+
Encrypted ID Token *hints* remain unsupported, matching the measured reference
|
|
62
|
+
behavior; clients can decrypt an issued ID Token before using its signed inner
|
|
63
|
+
token as a hint. This is distinct from encrypted output support.
|
|
64
|
+
|
|
65
|
+
Evidence: [Ruby regression](validation/id-token-recipient-encryption-current.txt),
|
|
66
|
+
[three databases](validation/id-token-recipient-encryption-matrix.txt),
|
|
67
|
+
[Node jose independent decryption](validation/id-token-recipient-encryption-jose.txt),
|
|
68
|
+
and [reference OP](validation/node-encrypted-id-tokens.txt).
|
|
69
|
+
The cross-library probe covers 36 algorithm combinations, initial issuance and
|
|
70
|
+
refresh, inner signature verification and ciphertext tampering. Its temporary
|
|
71
|
+
export includes an ephemeral synthetic recipient private key and must be removed
|
|
72
|
+
after checking; it is not a committed fixture. Run the Ruby test
|
|
73
|
+
`test_ciba_id_token_recipient_encryption` with `CIBA_ENCRYPTED_ID_TOKEN_EXPORT`
|
|
74
|
+
set to a temporary path, then run `verify-gem-encrypted-id-tokens.mjs` with that
|
|
75
|
+
path in the reference Node environment.
|
|
76
|
+
|
|
77
|
+
This is an initial subset of node-oidc-provider encryption capabilities. ECDH,
|
|
78
|
+
symmetric recipient encryption and all metadata-management/remote-key
|
|
79
|
+
combinations remain outstanding.
|
|
80
|
+
|
|
81
|
+
`examples/encrypted_id_token_smoke.rb` now runs from `bin/verify-package` after
|
|
82
|
+
installing the built gem into a temporary directory. It exercises RS256,
|
|
83
|
+
Ed25519 and EdDSA signatures with RSA-OAEP-256/A256GCM, opaque/JWT access tokens,
|
|
84
|
+
initial issuance and refresh, recipient decryption and inner signature checks.
|
|
85
|
+
It uses Rack requests and inline recipient keys; this does not establish remote
|
|
86
|
+
HTTPS recipient retrieval. [Package evidence](validation/id-token-encryption-installed.txt).
|
|
87
|
+
[Ruby 3.3/3.4 regression](validation/id-token-encryption-rubies.txt) covers the
|
|
88
|
+
full suite on SQLite; two row-lock tests remain skipped on that database.
|
|
89
|
+
|
|
90
|
+
## HTTPS recipient retrieval and failure atomicity
|
|
91
|
+
|
|
92
|
+
The initial encryption path unintentionally used upstream's unguarded HTTP
|
|
93
|
+
lookup during issuance. The [installed regression before correction](validation/id-token-encryption-https-red.txt)
|
|
94
|
+
shows a forbidden loopback recipient being contacted and a token issued.
|
|
95
|
+
The HTTP wrapper now covers CIBA issuance/refresh recipient retrieval as well
|
|
96
|
+
as assertion/request keys. Upstream HTTP authorization failures during issuance
|
|
97
|
+
raise an exception rather than halting with a response inside the transaction;
|
|
98
|
+
this preserves source rollback.
|
|
99
|
+
|
|
100
|
+
The [installed HTTPS probe](validation/id-token-encryption-https-installed.txt)
|
|
101
|
+
runs with an explicitly trusted local certificate and a separate untrusted
|
|
102
|
+
process. It verifies default loopback denial, explicit fixture-only allowance,
|
|
103
|
+
TLS certificate rejection before the handler, GET without forwarded credentials,
|
|
104
|
+
old-key cache retention, new-key use after explicit cache eviction, 503/malformed
|
|
105
|
+
JSON failure rollback and subsequent recovery/refresh rotation in opaque and
|
|
106
|
+
JWT access-token modes. Recipient decryption and the inner RSA signature are
|
|
107
|
+
checked throughout. Real TTL passage is not measured; eviction is explicit.
|
|
108
|
+
The private key never leaves the test recipient.
|
|
109
|
+
|
|
110
|
+
[Database regressions](validation/id-token-encryption-remote-matrix.txt) also
|
|
111
|
+
exercise redirects and oversized bodies during initial issuance and refresh.
|
|
112
|
+
These use HTTP fixtures with an explicit loopback allowance; TLS evidence comes
|
|
113
|
+
from the installed probe. The earlier supported-Ruby matrix predates this HTTP
|
|
114
|
+
correction. Broader signing/encryption combinations and deployment conditions
|
|
115
|
+
remain outstanding; remote-cache and management evidence follows below.
|
|
116
|
+
|
|
117
|
+
After the HTTP correction, [full Ruby 4.0.6 regression](validation/id-token-encryption-https-full.txt)
|
|
118
|
+
passes 348 tests /5,576 assertions on SQLite with two row-lock skips; the
|
|
119
|
+
targeted three-DB matrix passes seven tests /802 assertions per database.
|
|
120
|
+
|
|
121
|
+
## Reference remote cache and management comparison
|
|
122
|
+
|
|
123
|
+
`test/reference/full-op/encryption-remote.mjs` exercises actual reference HTTP
|
|
124
|
+
endpoints and key retrieval with a narrowly allowed local fixture URL. Live
|
|
125
|
+
cache retains the old recipient; explicit expiry retrieves the replacement.
|
|
126
|
+
Recipient decryption and inner signature/issuer/audience/subject are verified.
|
|
127
|
+
No Authorization header reaches the key server.
|
|
128
|
+
[Reference execution](validation/node-encryption-remote.txt).
|
|
129
|
+
|
|
130
|
+
One concrete difference remains after failed recipient-key refresh. With 503
|
|
131
|
+
and valid JSON, the reference first returns 400 invalid_client_metadata, then
|
|
132
|
+
uses retained keys for the next token refresh without refetching while the key
|
|
133
|
+
endpoint still fails. The gem rolls back and returns 500 server_error; fetch
|
|
134
|
+
failures are not inserted as successful cache entries. Invalid JSON causes
|
|
135
|
+
repeated refetch/failure in both implementations, with those different error
|
|
136
|
+
codes; repairing the response restores issuance.
|
|
137
|
+
|
|
138
|
+
The reference `ClientKeyStore.refresh` updates freshness before checking HTTP
|
|
139
|
+
status, but parses JSON before updating freshness. This explains the observed
|
|
140
|
+
difference. It also imposes at least 60 seconds of freshness; the gem's upstream
|
|
141
|
+
HTTP cache honors no-cache without this floor. These probes force expiry rather
|
|
142
|
+
than waiting for real TTL. Lifetime and post-failure key retention are unresolved
|
|
143
|
+
alignment differences, not claimed equivalence or an approved new adaptation.
|
|
144
|
+
No cache behavior was changed by this comparison.
|
|
145
|
+
|
|
146
|
+
The reference also dynamically registers an encrypted client and updates its
|
|
147
|
+
inline recipient via the management endpoint. Invalid encryption metadata leaves
|
|
148
|
+
the old key intact; successful replacement changes the recipient for existing
|
|
149
|
+
refresh credentials while the original token still decrypts with the old key.
|
|
150
|
+
Gem tests `test_ciba_encryption_management_replaces_recipient_opaque` and
|
|
151
|
+
`test_ciba_encryption_management_replaces_recipient_jwt` verify those boundaries
|
|
152
|
+
in both access-token modes. [Database execution](validation/id-token-encryption-management-matrix.txt).
|
|
153
|
+
This does not prove every management transition, remote URI change or concurrent
|
|
154
|
+
registration update.
|
|
155
|
+
|
|
156
|
+
### Defaults, removal and disabled registration
|
|
157
|
+
|
|
158
|
+
The management probes now also omit enc while retaining alg: both implementations
|
|
159
|
+
replace the previous A256GCM selection with A128CBC-HS256 and encrypt subsequent
|
|
160
|
+
refresh responses accordingly. Removing both encryption fields clears the
|
|
161
|
+
stored requirement and returns signed ID Tokens on existing refresh credentials.
|
|
162
|
+
The tests follow rotated registration access tokens after each successful PUT;
|
|
163
|
+
reusing the previous management token correctly fails authentication.
|
|
164
|
+
|
|
165
|
+
Disabled-capability registration originally stored and returned the encryption
|
|
166
|
+
fields, although issuance could not honor them. An initial proposal to reject
|
|
167
|
+
registration was revised after the reference returned 201 while omitting both
|
|
168
|
+
fields. The implementation now ignores them, matching the reference's response
|
|
169
|
+
and stored client. [Reference registration](validation/node-encryption-disabled-registration.txt),
|
|
170
|
+
[reference updates](validation/node-encryption-remote.txt),
|
|
171
|
+
[final DB matrix](validation/id-token-encryption-config-matrix.txt).
|
|
172
|
+
This correction is confined to CIBA registration; ordinary OAuth clients follow
|
|
173
|
+
their existing upstream handling.
|
|
174
|
+
|
|
175
|
+
## Client-secret-derived encryption
|
|
176
|
+
|
|
177
|
+
`dir`, AES-KW and AES-GCMKW do not require recipient JWKS. The OP derives
|
|
178
|
+
bytes from the UTF-8 client secret using SHA-256 for lengths up to32 bytes,
|
|
179
|
+
SHA-384 up to48, and SHA-512 up to64, truncating to the required length. AES-KW
|
|
180
|
+
uses the wrapping-key size; `dir` uses the content-encryption key size (the
|
|
181
|
+
full combined MAC/encryption size for CBC-HS). This follows the pinned
|
|
182
|
+
node-oidc-provider9.12.2 client model and its actual issuance/refresh output.
|
|
183
|
+
It is distinct from HMAC signing, which uses the original secret directly.
|
|
184
|
+
|
|
185
|
+
`ciba_id_token_encryption_secret(oauth_application:)` retrieves the original
|
|
186
|
+
secret and by default delegates to `ciba_id_token_hmac_secret`. Verified
|
|
187
|
+
Basic/POST credentials can be reused within that authenticated request even
|
|
188
|
+
with hash storage. Explicit plaintext storage is the fallback. Applications
|
|
189
|
+
using private-key authentication with hashed secrets must supply an original
|
|
190
|
+
secret through the callback. A missing, empty or invalid-encoding secret fails
|
|
191
|
+
issuance and rolls back consumption; the hash is never used as the secret.
|
|
192
|
+
|
|
193
|
+
The [reference probe](validation/node-encryption-symmetric.txt) verifies42
|
|
194
|
+
combinations, including AES-GCMKW, without recipient JWKS. The initial symmetric increment
|
|
195
|
+
implemented24 combinations (`dir`/AES-KW × six content methods); the
|
|
196
|
+
[local tests](validation/symmetric-encryption-current.txt) verify hashed-secret
|
|
197
|
+
DCR, initial/refresh issuance, wrong keys and missing-secret rollback.
|
|
198
|
+
[Independent jose verification](validation/symmetric-encryption-jose.txt)
|
|
199
|
+
decrypts48 actual gem tokens and verifies their inner RS256 signatures.
|
|
200
|
+
The standalone verifier takes a disposable vector file exported with
|
|
201
|
+
`CIBA_SYMMETRIC_EXPORT`; synthetic secrets must not be committed.
|
|
202
|
+
|
|
203
|
+
ECDH is now implemented by the CIBA-local adapter described below. The `jwe`1.1.1 `VALID_ALG` list
|
|
204
|
+
names ECDH and AES-GCMKW, but its algorithm dispatcher has no implementations.
|
|
205
|
+
AES-GCMKW is now provided by the CIBA-local adapter described below; ECDH is
|
|
206
|
+
handled separately. Broader authentication/signature combinations remain pending. Existing RSA evidence does not establish those cases.
|
|
207
|
+
|
|
208
|
+
The [three-DB encryption subset](validation/symmetric-encryption-matrix.txt)
|
|
209
|
+
passes10 tests/1,211 assertions on each SQLite/PostgreSQL/MySQL. A subsequent source-review
|
|
210
|
+
found that upstream `oidc.rb#generate_id_token` eagerly calls
|
|
211
|
+
`oauth_application_jwks` before encoding. The [regression reproduction](validation/symmetric-jwks-red.txt)
|
|
212
|
+
confirmed that an unrelated503 JWKS URI incorrectly prevented symmetric issuance.
|
|
213
|
+
A CIBA-only generation scope now skips that recipient lookup for symmetric
|
|
214
|
+
algorithms and restores the previous scope on success or exception. Authentication
|
|
215
|
+
outside generation still resolves and validates the registered public key.
|
|
216
|
+
The [targeted HTTP tests](validation/symmetric-jwks-current.txt) verify zero
|
|
217
|
+
recipient requests during Basic issuance/refresh and actual private_key_jwt
|
|
218
|
+
retrieval with wrong-key rejection and recovery. The [reference probe](validation/node-symmetric-jwks.txt)
|
|
219
|
+
verifies84 combinations (42 without JWKS and42 with a503 URI), all without
|
|
220
|
+
recipient retrieval. This does not suppress asymmetric encryption or ordinary OP
|
|
221
|
+
key retrieval.
|
|
222
|
+
|
|
223
|
+
The [full local regression](validation/symmetric-encryption-full.txt) passes356 tests/6,261 assertions, with two SQLite row-lock skips.
|
|
224
|
+
|
|
225
|
+
The updated [three-DB subset](validation/symmetric-jwks-matrix.txt) passes21
|
|
226
|
+
tests/1,505 assertions per DB, including encryption and client assertions.
|
|
227
|
+
The [installed artifact](validation/symmetric-encryption-installed.txt) passes16
|
|
228
|
+
symmetric cases across four key algorithms, Basic with hashed storage / POST
|
|
229
|
+
with plaintext storage, and opaque/JWT access tokens. It decrypts and verifies
|
|
230
|
+
initial and refresh ID Tokens while an unrelated JWKS URI is configured and
|
|
231
|
+
any outbound recipient HTTP call would fail the smoke. Existing RSA and TLS
|
|
232
|
+
package checks also pass. The build reuses installed dependencies; network
|
|
233
|
+
resolution of dependencies is not established.
|
|
234
|
+
|
|
235
|
+
[Supported Ruby regression](validation/symmetric-encryption-rubies.txt) passes358 tests/6,274 assertions on each Ruby3.3 and3.4, with two SQLite row-lock skips.
|
|
236
|
+
|
|
237
|
+
## AES-GCM key wrapping
|
|
238
|
+
|
|
239
|
+
The CIBA-local adapter supports A128GCMKW/A192GCMKW/A256GCMKW. It uses OpenSSL
|
|
240
|
+
AES-GCM to wrap a newly generated content key, with a fresh12-byte random IV,
|
|
241
|
+
empty wrapping AAD and a16-byte authentication tag. Both wrapping parameters
|
|
242
|
+
are included in the protected header, which the JWE backend authenticates as
|
|
243
|
+
content AAD. Content encryption and compact serialization remain in `jwe`;
|
|
244
|
+
there is no global algorithm registration or new token-decryption endpoint.
|
|
245
|
+
The client-secret derivation above also applies to GCMKW.
|
|
246
|
+
[RFC7518 §4.7](https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7).
|
|
247
|
+
|
|
248
|
+
The [registration red test](validation/gcmkw-red.txt) failed before the adapter.
|
|
249
|
+
The expanded [local symmetric tests](validation/gcmkw-current.txt) pass42
|
|
250
|
+
algorithm/content combinations, checking DCR with hashed storage, initial and
|
|
251
|
+
refresh output, wrapping IV/tag lengths and distinct sampled IVs. This sampling
|
|
252
|
+
is not a guarantee of collision-free operation; randomness comes from
|
|
253
|
+
`SecureRandom` for each wrap and from the JWE backend for content keys/IVs.
|
|
254
|
+
The [independent jose verifier](validation/gcmkw-jose.txt) decrypts84 actual gem
|
|
255
|
+
tokens, verifies the inner RS256 signature, rejects wrong keys, and rejects
|
|
256
|
+
changes to wrapping IV/tag, encrypted key, ciphertext and content tag.
|
|
257
|
+
|
|
258
|
+
The [reference execution](validation/node-symmetric-jwks.txt) already covers
|
|
259
|
+
all42 combinations with and without an unrelated503 JWKS URI. The updated [installed-artifact smoke](validation/gcmkw-installed.txt) covers
|
|
260
|
+
all seven symmetric key algorithms in28 configurations: hashed Basic/plaintext
|
|
261
|
+
POST and opaque/JWT access tokens. Initial and refresh tokens are unwrapped,
|
|
262
|
+
decrypted and signature-checked, with unexpected recipient HTTP calls rejected.
|
|
263
|
+
Supported-Ruby validation is recorded separately below.
|
|
264
|
+
|
|
265
|
+
The [GCMKW encryption regression subset](validation/gcmkw-matrix.txt) passes17 tests/1,794 assertions on each SQLite/PostgreSQL/MySQL. The [full local regression](validation/gcmkw-full.txt) passes358 tests/6,724 assertions with two SQLite row-lock skips.
|
|
266
|
+
|
|
267
|
+
The [GCMKW supported-Ruby regression](validation/gcmkw-rubies.txt) passes358 tests/6,724 assertions on each Ruby3.3 and3.4, with two SQLite row-lock skips. The [ECDH contract](research/ecdh-encryption-reference-contract.md) records96 reference configurations and the new adapter with its remaining validation gates.
|
|
268
|
+
|
|
269
|
+
## ECDH recipient keys
|
|
270
|
+
|
|
271
|
+
ECDH-ES and ECDH-ES+A128KW/A192KW/A256KW accept registered public P-256,
|
|
272
|
+
P-384, P-521 or X25519 recipient keys. All six content methods above are
|
|
273
|
+
supported. Public JWKS/jwks_uri is required; client-secret retrieval is not used.
|
|
274
|
+
For ECDH public recipients, `key_ops` must be omitted or empty, matching the
|
|
275
|
+
reference's public-key import behavior. Nonempty declarations fail issuance; see
|
|
276
|
+
the [metadata comparison](research/ecdh-encryption-reference-contract.md#recipient-key-metadata).
|
|
277
|
+
The protected header includes a fresh public epk, recipient kid when present,
|
|
278
|
+
cty/issuer/audience. Direct ECDH has an empty encrypted-key segment; wrapping
|
|
279
|
+
variants use the existing AES-KW backend. Unsupported curves and invalid key
|
|
280
|
+
agreement fail instead of returning plaintext. See the [reference and current
|
|
281
|
+
validation contract](research/ecdh-encryption-reference-contract.md).
|
|
282
|
+
|
|
283
|
+
This is an initial implementation. [Installed-artifact validation](validation/ecdh-installed.txt)
|
|
284
|
+
now covers32 ECDH configurations. [Ruby3.3/3.4](validation/ecdh-rubies.txt) each pass361 tests/9,074 assertions.
|
|
285
|
+
ECDH-specific HTTP/HTTPS key retrieval, failure/rotation and sequential management
|
|
286
|
+
now pass ([contract](research/ecdh-encryption-reference-contract.md#gem-remote-retrieval-and-management)); real TTL is now [measured](research/ecdh-encryption-reference-contract.md#actual-cache-expiry-without-mutation), with a shorter-max-age policy difference; [management/issuance overlap](research/ecdh-encryption-reference-contract.md#management-update-overlapping-issuance) now has reference and three-DB snapshot evidence.
|
|
287
|
+
|
|
288
|
+
Recipient selection follows explicit alg/use priority and stable input order. Cryptographic validation occurs after selecting the recipient: an unusable preferred key fails issuance without trying another key. [Comparison and regression evidence](research/ecdh-encryption-reference-contract.md#multiple-recipients-and-invalid-preferred-keys).
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Optional ID Token hints
|
|
2
|
+
|
|
3
|
+
Enable `ciba_id_token_hint_enabled true` and implement
|
|
4
|
+
`resolve_ciba_id_token_subject(subject, oauth_application:)`. The callback receives
|
|
5
|
+
a cryptographically verified subject (public or enabled pairwise) and the authenticated client record;
|
|
6
|
+
return the corresponding local account ID or nil. Do not assume custom public
|
|
7
|
+
subjects are database primary keys. Normal account eligibility and subsequent
|
|
8
|
+
approval/account/client binding checks still apply. No migration is needed.
|
|
9
|
+
|
|
10
|
+
Exactly one of `login_hint`, enabled `login_hint_token`, or enabled `id_token_hint`
|
|
11
|
+
is accepted. ID Token hints are limited to 8192 bytes. Raw hints are removed before
|
|
12
|
+
request persistence, transaction hooks, device snapshots and observation events.
|
|
13
|
+
Applications must separately redact incoming HTTP parameters in their own logs.
|
|
14
|
+
|
|
15
|
+
The dedicated hint verifier uses the authenticated client's configured ID Token
|
|
16
|
+
signing algorithm, falling back to the OP's first signing algorithm as the issuer
|
|
17
|
+
does. It checks signature, issuer, audience membership and a string subject.
|
|
18
|
+
Like the reference hint verifier, it ignores expiration and does not require iat
|
|
19
|
+
or exp. If present, iat/nbf must be numeric; fractional values are accepted. Future
|
|
20
|
+
nbf is rejected with the configured leeway. Future iat is rejected only when exp
|
|
21
|
+
is absent. A present jti must be a string. Supported algorithm families
|
|
22
|
+
are RS, PS, ES and HS; all twelve256/384/512 variants have actual issuance,
|
|
23
|
+
current/expired reuse and wrong-key rejection tests.
|
|
24
|
+
Unsigned and encrypted hints are not supported. A valid nested encrypted hint
|
|
25
|
+
is rejected before subject resolution, matching the measured node-oidc-provider
|
|
26
|
+
9.12.2 CIBA behavior even with its encryption feature and OP key configured.
|
|
27
|
+
|
|
28
|
+
Asymmetric keys come from `oauth_jwt_public_keys[algorithm]`, falling back to
|
|
29
|
+
`oauth_jwt_keys[algorithm]` only when no public-key entry exists. Arrays retain
|
|
30
|
+
old verification keys. A supplied kid must match the trusted key's JWT::JWK kid;
|
|
31
|
+
without kid all configured keys for the pinned algorithm may be tried. No token
|
|
32
|
+
URL or embedded key is fetched or trusted. Removing a key stops acceptance of
|
|
33
|
+
hints signed by it. CIBA HMAC issuance and hint verification use the authenticated
|
|
34
|
+
client's secret, matching the reference. See the secret retrieval contract below.
|
|
35
|
+
|
|
36
|
+
Expiration is ignored **only in this dedicated hint verifier**. By default
|
|
37
|
+
`ciba_id_token_hint_max_age nil` leaves age unrestricted while keys remain
|
|
38
|
+
trusted, matching the measured node behavior. Set a positive integer number of
|
|
39
|
+
seconds to limit age from iat. This is separate from expiration and does not
|
|
40
|
+
alter normal access-token or client-assertion validation.
|
|
41
|
+
|
|
42
|
+
The hint path now matches the measured reference's azp and typ handling: neither
|
|
43
|
+
is used to reject a hint. Registered algorithm, trusted key, issuer and client
|
|
44
|
+
audience remain mandatory. The recognized JOSE critical declaration b64=true
|
|
45
|
+
is accepted; unknown critical extensions and unencoded JWT payloads are rejected.
|
|
46
|
+
This change is isolated to hints; access-token, signed-request and client-assertion
|
|
47
|
+
validation is unchanged. With an explicit ciba_id_token_hint_max_age, iat is
|
|
48
|
+
required to evaluate that application age policy. Optional [pairwise subjects](pairwise.md)
|
|
49
|
+
use the same resolver boundary to map the verified subject to a canonical account.
|
|
50
|
+
|
|
51
|
+
The reference even ignores the type of exp in this identity-hint path. This is
|
|
52
|
+
not a contract for issuing malformed ID Tokens or accepting them as authentication
|
|
53
|
+
results. The regression re-signs controlled variants and proves they still only
|
|
54
|
+
create pending requests. [Reference](validation/node-id-hint-validation-profile.txt),
|
|
55
|
+
[three-DB regression](validation/id-hint-validation-matrix.txt).
|
|
56
|
+
|
|
57
|
+
Successful hint validation creates a pending request, not an approval or token.
|
|
58
|
+
New customer authentication/approval remains mandatory. See the
|
|
59
|
+
[reference measurements](research/id-token-hint-reference-contract.md) and
|
|
60
|
+
[local tests](validation/id-token-hint-tests.txt). The later
|
|
61
|
+
[encrypted-hint boundary test](validation/encrypted-hint-boundary.txt) and
|
|
62
|
+
pairwise integration tests extend that evidence. Edwards ID Token support is explicitly configured as described below; arbitrary
|
|
63
|
+
application resolver correctness remains outside the verified scope.
|
|
64
|
+
|
|
65
|
+
## Complete configured RS/PS/ES/HS matrix
|
|
66
|
+
|
|
67
|
+
The updated test verifies each actually issued ID Token using its configured
|
|
68
|
+
key, issuer and audience, then tests current and expired reuse as hints. Neither
|
|
69
|
+
form inherits approval; a token signed by a different key is rejected before
|
|
70
|
+
request persistence. The HMAC client-secret negative case explicitly selects
|
|
71
|
+
HS256 first, so it tests wrong key ownership rather than only algorithm mismatch.
|
|
72
|
+
[Three databases](validation/id-hint-algorithms-matrix.txt): seven tests /229
|
|
73
|
+
assertions per DB, no failures/errors/skips.
|
|
74
|
+
|
|
75
|
+
The independent reference matrix now covers those twelve plus Ed25519/EdDSA
|
|
76
|
+
([execution](validation/node-id-hint-algorithms.txt)). Edwards issuance/hint verification was a concrete remaining gap; the initial
|
|
77
|
+
implementation below addresses it separately from request-object/client-assertion verification.
|
|
78
|
+
|
|
79
|
+
## Explicit Edwards ID Token keys
|
|
80
|
+
|
|
81
|
+
Configure `ciba_id_token_signing_keys` with `"Ed25519"` and/or `"EdDSA"` entries
|
|
82
|
+
containing an OpenSSL Ed25519 private key. Default is an empty hash. The client
|
|
83
|
+
must explicitly select the matching `id_token_signed_response_alg`; the ordinary
|
|
84
|
+
OP default signing algorithm is unchanged. Each entry may instead be an array:
|
|
85
|
+
the first key signs new ID Tokens, and later public/private keys are retained
|
|
86
|
+
for hint verification. The first key must be private and all keys must be Ed25519.
|
|
87
|
+
|
|
88
|
+
The shared JWKS publishes only public OKP material with RFC7638-derived kid, alg
|
|
89
|
+
and sig use; existing upstream keys remain present. Discovery adds the configured
|
|
90
|
+
ID Token algorithms. CIBA issuance signs with the selected key, and hints verify
|
|
91
|
+
with the same configured/retained public keys. Removing a retained key prevents
|
|
92
|
+
acceptance of its hints. Hint issuer/audience/subject/time checks and new-approval
|
|
93
|
+
requirements remain in force. This does not change JWT access-token signing.
|
|
94
|
+
|
|
95
|
+
The gem continues to include at_hash, using the left half of SHA-512 for Edwards.
|
|
96
|
+
The reference's CIBA token-endpoint default omits this optional claim; its
|
|
97
|
+
ID Token model maps both Edwards algorithm names to SHA-512 when hashing is
|
|
98
|
+
requested. This is not a claim that their default claim sets are identical.
|
|
99
|
+
|
|
100
|
+
Edwards ID Tokens can use the optional [recipient encryption](id-token-encryption.md)
|
|
101
|
+
path. Without that enabled capability, an encryption selection raises
|
|
102
|
+
ConfigurationError instead of emitting plaintext and keeps approval available.
|
|
103
|
+
Independent Node verification, installed artifact and supported-Ruby evidence for
|
|
104
|
+
signed output is recorded in the [contract](research/id-token-hint-reference-contract.md).
|
|
105
|
+
Encryption has its own narrower evidence and remaining integration work.
|
|
106
|
+
[Initial14-test three-DB regression](validation/edwards-id-token-matrix.txt),
|
|
107
|
+
[final Edwards/rollback cases](validation/edwards-id-token-final-matrix.txt).
|
|
108
|
+
|
|
109
|
+
## HMAC client-secret ownership
|
|
110
|
+
|
|
111
|
+
CIBA ID Tokens signed with HS256/HS384/HS512 now use the UTF-8 client secret for
|
|
112
|
+
both issuance and hint verification, as required by
|
|
113
|
+
[OIDC Core ID Token validation](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation).
|
|
114
|
+
The algorithm is still selected by registered `id_token_signed_response_alg`
|
|
115
|
+
or the upstream default, and must be configured among the supported signing
|
|
116
|
+
algorithms. OP-wide HS keys are not a fallback for a missing client secret.
|
|
117
|
+
Non-CIBA JWT encoding and JWT access-token signing retain upstream key handling.
|
|
118
|
+
|
|
119
|
+
After successful client_secret_basic/client_secret_post authentication, the
|
|
120
|
+
verified secret is retained only on that request's Rodauth instance, bound to
|
|
121
|
+
the authenticated application's ID. This works with hashed DB storage; no new
|
|
122
|
+
plaintext persistence is introduced. With explicitly configured plaintext
|
|
123
|
+
client-secret storage, other authentication methods can use the stored secret.
|
|
124
|
+
For hashed storage plus private_key_jwt/mTLS, supply an application secret lookup:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
ciba_id_token_hmac_secret do |oauth_application:|
|
|
128
|
+
secrets.fetch(oauth_application.fetch(:client_id)) # application's secret store
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The callback must return that client's original secret, not its password hash
|
|
133
|
+
or an OP signing key. Missing/non-string secrets and insufficient key length
|
|
134
|
+
fail issuance with rollback; they do not produce an ID Token with another key.
|
|
135
|
+
Minimum byte lengths are 32/48/64 respectively, following
|
|
136
|
+
[JWA section 3.2](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2);
|
|
137
|
+
secret entropy and secure storage remain application responsibilities.
|
|
138
|
+
Current-secret rotation changes both new issuance and accepted HMAC hints.
|
|
139
|
+
|
|
140
|
+
This replaces the unreleased implementation's OP-key HMAC behavior. Previously
|
|
141
|
+
issued OP-key HMAC hints are no longer accepted unless that key is actually the
|
|
142
|
+
client secret. Do not configure a shared OP key as every client's secret to
|
|
143
|
+
preserve those tokens.
|
|
144
|
+
|
|
145
|
+
[Three-DB tests](validation/hmac-id-token-matrix.txt) cover Basic/POST,
|
|
146
|
+
plaintext/hashed storage, all three HS algorithms, initial issuance/refresh,
|
|
147
|
+
current/expired hints, wrong OP key rejection and missing/short callback rollback.
|
|
148
|
+
[Full local regression](validation/hmac-id-token-full.txt) passed353 tests before
|
|
149
|
+
the additional registration-length case below.
|
|
150
|
+
|
|
151
|
+
The installed [HMAC smoke](../examples/hmac_id_token_smoke.rb) exercises all
|
|
152
|
+
three algorithms with plaintext/hashed secret storage and opaque/JWT access
|
|
153
|
+
tokens. It verifies initial issuance, refresh, hints and rejection of the OP key.
|
|
154
|
+
Replacing the stored client secret rejects old authentication and old HMAC hints;
|
|
155
|
+
existing refresh credentials issue with the new secret after new authentication.
|
|
156
|
+
This is a backend replacement fixture, not a standard secret-rotation endpoint
|
|
157
|
+
or a measurement of reference cache propagation.
|
|
158
|
+
[Installed artifact evidence](validation/hmac-id-token-installed.txt),
|
|
159
|
+
[Ruby 3.3/3.4 evidence](validation/hmac-id-token-rubies.txt).
|
|
160
|
+
|
|
161
|
+
### Dynamically generated secret length
|
|
162
|
+
|
|
163
|
+
Upstream's default generated secret is43 encoded bytes, insufficient for HS384
|
|
164
|
+
and HS512 signing. A [regression](validation/hmac-registration-secret-red.txt)
|
|
165
|
+
reproduced the HS384 failure. For CIBA registration only, the gem now replaces
|
|
166
|
+
an insufficient generated secret with fresh cryptographic randomness sized for
|
|
167
|
+
the selected algorithm, using the upstream secret setter to preserve configured
|
|
168
|
+
hash storage. HS256 and unrelated clients keep their existing generation path.
|
|
169
|
+
This applies whether or not management-token issuance is enabled.
|
|
170
|
+
[Three-DB regression](validation/hmac-registration-secret-matrix.txt) verifies
|
|
171
|
+
registration, hashed storage, authenticated CIBA issuance and client-secret
|
|
172
|
+
signature validation for all three algorithms. The
|
|
173
|
+
[reference registration fixture](validation/node-hmac-registration-secret.txt)
|
|
174
|
+
also produces secrets meeting the algorithm's minimum length.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Optional login_hint_token
|
|
2
|
+
|
|
3
|
+
The default profile continues to accept `login_hint`. Enable `login_hint_token` explicitly and supply an application resolver in the same configuration block:
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
ciba_login_hint_token_enabled true
|
|
7
|
+
resolve_ciba_login_hint_token do |token, oauth_application:|
|
|
8
|
+
# Your service validates the token and returns its local account ID or nil.
|
|
9
|
+
HintService.resolve(token, client_id: oauth_application[:client_id])
|
|
10
|
+
end
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`HintService` is an application example, not a class supplied by the gem. The resolver is required at configuration time when enabled. It receives the already-authenticated OAuth client. Like node-oidc-provider's `processLoginHintToken`, this API does not mandate a token format. It can accept an opaque reference or an application-defined signed token. The application owns authenticity, permitted issuer/audience/client, expiry, replay rules where needed and mapping to the OP's local account. For JWT formats, use appropriate signature/claim validation with trusted keys; decoding alone is not identity verification.
|
|
14
|
+
|
|
15
|
+
The Backchannel Authentication Endpoint requires exactly one supplied `login_hint`, enabled `login_hint_token`, or enabled `id_token_hint`. Empty, non-string, multiple or oversized hints return invalid_request. The token limit is 8,192 bytes, also bounded by the total form-body limit; ordinary login_hint remains limited to 1,024 bytes. This capability adds no custom Discovery field and needs no DB migration.
|
|
16
|
+
|
|
17
|
+
Return nil for a token that does not identify an eligible local account; the response is unknown_user_id. The gem also checks the resulting account is active. Resolver exceptions use the existing server-error boundary and do not create a request. Configuration does not replace the existing `resolve_ciba_login_hint`, which remains required for the default mechanism.
|
|
18
|
+
|
|
19
|
+
For an expired token, raise `Rodauth::CibaSupport::ProtocolError,
|
|
20
|
+
"expired_login_hint_token"` to return the corresponding HTTP400 response.
|
|
21
|
+
Other unexpected resolver failures produce HTTP500 rather than claiming that
|
|
22
|
+
the customer is unknown. The application must document accepted token formats,
|
|
23
|
+
issuers and age policy for its clients. Signature policy, single-use references
|
|
24
|
+
and discovery-service encrypted tokens are application implementations, not
|
|
25
|
+
built-in token formats provided by this gem.
|
|
26
|
+
|
|
27
|
+
After successful resolution, only the account binding enters the stored request. The raw token is removed before request hooks, persistence, device callbacks and observation events. The incoming Rack request necessarily still contains the submitted parameter; application/request logging must apply its own filtering. The reference OP retains raw hint parameters in its asynchronous request; discarding the value is this gem's deliberate storage adaptation.
|
|
28
|
+
|
|
29
|
+
Approval and saved-consent account/client checks remain unchanged. Token-endpoint parameters cannot re-resolve or replace the subject. Disabling this hint mechanism prevents new hint-token requests but does not invalidate already-resolved pending requests: those continue using their saved identity and ordinary completion/expiry/revocation checks.
|
|
30
|
+
|
|
31
|
+
## Evidence and scope
|
|
32
|
+
|
|
33
|
+
`test/ciba_hint_token_test.rb` verifies opt-in/resolver requirements, invalid shapes/size, mixed hints, client-aware resolver behavior, active-account and approval identity checks, raw-token exclusion from DB/snapshots/events, poll tampering and completion after deactivation. The complete node HTTP harness checks an application-defined opaque hint, client policy, mixed hints, unknown user and the signed ID Token subject. The installed-gem extension smoke now uses this mechanism with claims/resource/RAR.
|
|
34
|
+
|
|
35
|
+
These tests validate the protocol-to-application boundary, not an arbitrary production token format. The fixture resolver recognizes fixed local test references; it is not a reusable production verifier. [Signed authentication requests](signed-requests.md) and [ID Token hints](id-token-hint.md) have separate opt-in implementations and evidence.
|
data/docs/max-age.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# CIBA maximum authentication age
|
|
2
|
+
|
|
3
|
+
The gem accepts `max_age` at the backchannel endpoint as a node-oidc-provider
|
|
4
|
+
extension. This does not add a CIBA Core conformance requirement.
|
|
5
|
+
|
|
6
|
+
An explicit request value overrides client `default_max_age`. Omission or an empty
|
|
7
|
+
string uses the registered default, if any. Signed requests use only their signed
|
|
8
|
+
max_age; an unsigned outer value cannot override or supply it.
|
|
9
|
+
|
|
10
|
+
The accepted maximum age is saved with the request before device dispatch. The
|
|
11
|
+
application receives it through `trigger_ciba_authentication_device` and can read
|
|
12
|
+
it later using `ciba_request`:
|
|
13
|
+
|
|
14
|
+
- `nil`: no maximum age was selected.
|
|
15
|
+
- `0`: require fresh authentication.
|
|
16
|
+
- A positive integer: maximum authentication age in seconds.
|
|
17
|
+
|
|
18
|
+
Node retains positive inputs as strings in request.params; zero becomes its
|
|
19
|
+
internal `prompt=login`. The Ruby API uses an integer, retaining zero explicitly,
|
|
20
|
+
so applications need no second prompt field to distinguish fresh authentication
|
|
21
|
+
from no policy. This is an internal representation adaptation, not an HTTP response
|
|
22
|
+
change. Client metadata changes after acceptance do not alter the saved policy.
|
|
23
|
+
Applications must enforce freshness during authentication; approval does not reject
|
|
24
|
+
an old supplied auth_time automatically. Selected max_age causes auth_time to be
|
|
25
|
+
selected for ID Token output, but does not invent an authentication time.
|
|
26
|
+
Refresh preserves the original authentication claim selection/context and does
|
|
27
|
+
not start a new authentication request or reapply current client defaults.
|
|
28
|
+
|
|
29
|
+
## Parsing and migration
|
|
30
|
+
|
|
31
|
+
Validation follows the measured Node numeric boundary: a finite, non-negative
|
|
32
|
+
safe integer (at most9007199254740991) after numeric conversion. Integral decimal
|
|
33
|
+
and exponent representations and unsigned hexadecimal/binary/octal representations
|
|
34
|
+
are accepted. Whitespace-only input and negative zero mean zero. Unsupported
|
|
35
|
+
syntax, fractional results, negative numbers, nonfinite numbers and arrays are
|
|
36
|
+
rejected before saving or dispatching. The gem bounds the input to1024 UTF-8 bytes.
|
|
37
|
+
|
|
38
|
+
New request tables include nullable BIGINT `max_age`. Existing development schemas
|
|
39
|
+
must add it explicitly before upgraded workers start:
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
Rodauth::CibaSupport::Schema.add_request_max_age(db)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Pass `table:` / `column:` and configure `ciba_request_columns[:max_age]` for custom
|
|
46
|
+
storage. Startup requires this column. Existing requests remain NULL; migration
|
|
47
|
+
does not reinterpret them using today's client defaults. This migration is in
|
|
48
|
+
addition to the [authentication_claims migration](authentication-claims.md).
|
|
49
|
+
No refresh-table max_age column is needed.
|
|
50
|
+
|
|
51
|
+
## Evidence
|
|
52
|
+
|
|
53
|
+
The [pinned reference test](../test/reference/full-op/max-age.mjs) checks request
|
|
54
|
+
overrides, numeric forms, zero-to-login conversion, invalid values and successful
|
|
55
|
+
signed ID Tokens using an application-supplied old authTime. See its
|
|
56
|
+
[execution](validation/node-max-age.txt). Gem tests cover saved/frozen callback
|
|
57
|
+
policy, later metadata changes, signed input isolation, rejected values, custom
|
|
58
|
+
columns and forward migration. No deployment authentication policy is verified.
|
|
59
|
+
|
|
60
|
+
Validation: [Ruby3.3 / SQLite full suite](validation/max-age-ruby33.txt) passes391
|
|
61
|
+
tests /10,418 assertions with two row-lock skips. The [Ruby3.4 three-DB subset](validation/max-age-matrix.txt)
|
|
62
|
+
passes22 tests /1,317 assertions on each of SQLite/PostgreSQL/MySQL. Containers
|
|
63
|
+
reuse existing dependency images and mount current source read-only.
|
|
64
|
+
The [installed artifact smoke](validation/max-age-installed.txt) also passes;
|
|
65
|
+
it reuses locally installed dependencies rather than testing network resolution.
|