rodauth-ciba 0.1.0 → 0.2.1
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 +4 -4
- data/CHANGELOG.md +37 -1
- data/README.md +4 -4
- data/docs/api.md +8 -5
- data/docs/authentication-claims.md +11 -1
- data/docs/ciba-conformance.md +58 -0
- data/docs/ciba-core-requirements.json +3537 -0
- data/docs/claims.md +5 -1
- data/docs/max-age.md +3 -1
- data/docs/operations.md +28 -1
- data/docs/pairwise.md +20 -4
- data/docs/refresh-tokens.md +32 -0
- data/docs/release-readiness.md +22 -3
- data/docs/release-validation.md +5 -0
- data/docs/requirements-audit-current.json +2 -1
- data/docs/requirements-audit-current.md +2 -0
- data/examples/extensions_smoke.rb +18 -1
- data/lib/rodauth/ciba/pairwise.rb +7 -3
- data/lib/rodauth/ciba/registration.rb +4 -0
- data/lib/rodauth/ciba/version.rb +1 -1
- data/lib/rodauth/features/ciba.rb +195 -19
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5712de096045c1433694695a5ced5e8f2fb10133448f1b7a9a4c9765ce162fbb
|
|
4
|
+
data.tar.gz: a593cf64b2dd4f0886843238ff30147f9a24a6c1f0f13ade898aeaf204eef477
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2ede7d3a6d8235fc8ab8fbded2edbc49e492c036fa95573055fd8b25b27811536c3bf56443be2cdbbfbe23423c65e33f735943c67628d4c48c3da9709c1f500c
|
|
7
|
+
data.tar.gz: 1b8a08dc2b16a479e58c200bd46a3c2fb2d3957a4d3a610e2f5991e562c674d75286983afc982b96578eae9c060228275a5e497ee8b05dfccee1bc6e2bb797d5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,42 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
Prepared on 2026-10-22. Incorporates the unpublished 0.2.0 candidate and corrects
|
|
6
|
+
its stale documentation. The existing v0.2.0 tag is retained; no 0.2.0 gem was
|
|
7
|
+
published.
|
|
8
|
+
|
|
9
|
+
The official OpenID Conformance Suite has not been executed. Refresh tokens,
|
|
10
|
+
pairwise subjects and dynamic CIBA registration remain experimental and disabled
|
|
11
|
+
by default. This release does not claim certification or full CIBA conformance.
|
|
12
|
+
Do not run old and new workers together during this upgrade.
|
|
13
|
+
|
|
14
|
+
- Keep initial and refreshed token identity/key metadata bound to the successful
|
|
15
|
+
client authentication snapshot across concurrent registration updates.
|
|
16
|
+
- Preserve issued UserInfo subjects after a client changes its subject_type,
|
|
17
|
+
including opaque/JWT tokens without the optional claims capability.
|
|
18
|
+
|
|
19
|
+
- Bind refresh ID Tokens to the actual initial issuer/subject/audience and
|
|
20
|
+
disclosed auth_time using versioned JSON in existing storage. Preserve the
|
|
21
|
+
context through rotation and pairwise sector changes; omit refresh nonce.
|
|
22
|
+
- Reject legacy or invalid refresh contexts before hooks or mutations. This
|
|
23
|
+
requires reauthentication for old refresh tokens and coordinated worker updates;
|
|
24
|
+
no automatic data migration or new database column is introduced.
|
|
25
|
+
|
|
26
|
+
- Enforce requested public/pairwise subjects and essential ACR at approval,
|
|
27
|
+
issuance and refresh; preserve ordinary value/values filters on issued tokens.
|
|
28
|
+
- Keep pairwise UserInfo subjects bound to their issuance and prevent mandatory
|
|
29
|
+
nonce or signed UserInfo claims from bypassing qualifier validation.
|
|
30
|
+
- Accept positive integral JSON numbers in signed requested_expiry.
|
|
31
|
+
|
|
32
|
+
- Measure CIBA request expiry from handler receipt before client/hint validation,
|
|
33
|
+
without extending it for synchronous processing delays.
|
|
34
|
+
- Require saved authentication time for accepted max_age and essential auth_time
|
|
35
|
+
requests during approval, issuance and refresh without inventing timestamps.
|
|
36
|
+
- Add a responsibility-separated Core final requirement ledger and explicit
|
|
37
|
+
conformance-suite acceptance gates; no certification or full-conformance claim.
|
|
38
|
+
|
|
39
|
+
## 0.1.0 - 2026-10-11
|
|
4
40
|
|
|
5
41
|
First release of the CIBA OpenID Provider extension for rodauth-oauth.
|
|
6
42
|
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# rodauth-ciba
|
|
2
2
|
|
|
3
|
-
CIBA OpenID Provider extension for **rodauth-oauth**, with default poll and optional ping.
|
|
3
|
+
CIBA OpenID Provider extension for **rodauth-oauth**, with default poll and optional ping. This checkout documents version 0.2.1. Check [RubyGems](https://rubygems.org/gems/rodauth-ciba/versions) for published versions. See the [upgrade notes](docs/release-readiness.md#upgrade-to-021) before upgrading from 0.1.0.
|
|
4
4
|
|
|
5
5
|
It adds a backchannel authentication endpoint, Discovery metadata, Ruby approval/denial APIs, transactional hooks and post-commit observation events. Your app handles customer authentication, consent UI, notification transport, business authorization and any later delegation.
|
|
6
6
|
|
|
@@ -19,12 +19,12 @@ Optional [signed authentication requests](docs/signed-requests.md) use registere
|
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
22
|
-
Ruby>=3.3 is required.
|
|
22
|
+
Ruby>=3.3 is required. To build version 0.2.1 from this checkout:
|
|
23
23
|
|
|
24
24
|
```sh
|
|
25
25
|
bundle install
|
|
26
26
|
gem build rodauth-ciba.gemspec
|
|
27
|
-
gem install ./rodauth-ciba-0.1.
|
|
27
|
+
gem install ./rodauth-ciba-0.2.1.gem
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
The initial dependency bounds are intentionally narrow: rodauth-oauth1.7.0, Rodauth2.28.x, Sequel5.108.x and JWT3.3.x. Your application supplies its DB adapter and existing Rodauth/OAuth schema.
|
|
@@ -99,7 +99,7 @@ ruby bin/verify-package # build, install outside checkout, run all sm
|
|
|
99
99
|
|
|
100
100
|
The Docker runner builds the Ruby image, waits for database health, runs the same suite on each DB and removes its containers/network on exit. DB ports are not published. Data is on tmpfs; each test owns a random database. Only images/build cache remain. `pg` and `mysql2` are an optional Bundler group enabled inside Docker. The macOS/mise equivalent is `mise exec ruby -- ruby test/run.rb`.
|
|
101
101
|
|
|
102
|
-
[Release readiness](docs/release-readiness.md) records the current release checks and review limits. [Release validation](docs/release-validation.md) preserves historical results. The [
|
|
102
|
+
[Release readiness](docs/release-readiness.md) records the current release checks and review limits. [Release validation](docs/release-validation.md) preserves historical results. The [0.2.0 candidate CI run](https://github.com/kajisha/rodauth-ciba/actions/runs/39522218593) at commit `1ab56c35aead54158c3a38405da6ce3c2c443663` passed the Ruby 3.3/3.4/4.0 SQLite jobs and the Docker database job. CI results apply to the tested commit, not to later documentation or code changes. The official OpenID Conformance Suite has not been executed.
|
|
103
103
|
|
|
104
104
|
## License
|
|
105
105
|
|
data/docs/api.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Configuration and Ruby API (0.1
|
|
1
|
+
# Configuration and Ruby API (0.2.1)
|
|
2
2
|
|
|
3
3
|
Optional [login_hint_token](login-hint-token.md) uses `ciba_login_hint_token_enabled` and a required application resolver; no schema migration is needed.
|
|
4
4
|
|
|
@@ -217,10 +217,13 @@ Cleanup returns the deleted count. The cutoff must be an integer epoch second no
|
|
|
217
217
|
Implement `trigger_ciba_authentication_device(snapshot)` to initiate your application’s authentication/approval flow. It runs synchronously after the request transaction/savepoint finishes and before the successful Backchannel response. The frozen snapshot contains the saved internal request ID and account/client references, not the public auth_req_id or its digest. The callback may enqueue work or call the Ruby result API. It must return normally to allow a success response; it must not perform a Rack halt/throw. Its default raises `CibaSupport::ConfigurationError`, reported as HTTP 500 `server_error`. A deliberate no-op is appropriate only if the application already processes pending requests independently (as the local demo does).
|
|
218
218
|
|
|
219
219
|
Dispatch should enqueue work promptly instead of waiting for customer interaction.
|
|
220
|
-
Its synchronous execution consumes the saved request lifetime
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
220
|
+
Its synchronous execution consumes the saved request lifetime, measured from entry
|
|
221
|
+
to the backchannel handler before client/hint validation. `expires_in` remains
|
|
222
|
+
that lifetime, not seconds remaining at response. Slow dispatch can exhaust the
|
|
223
|
+
lifetime before acknowledgement; subsequent polling then returns expired_token.
|
|
224
|
+
The gem does not extend the deadline for that delay. This differs from the older
|
|
225
|
+
[measured reference timing](research/acknowledgement-time-contract.md). See
|
|
226
|
+
[conformance boundaries](ciba-conformance.md).
|
|
224
227
|
|
|
225
228
|
|
|
226
229
|
A callback exception goes to `report_ciba_processing_error`, not the observer-error reporter. Without a surrounding application transaction, the saved request remains: notification may already have happened, so failure cannot safely undo external effects. The client does not receive an auth_req_id on that failed response. A client retry creates a new request; the app must deduplicate notifications and expire/recover abandoned requests. This is not durable delivery.
|
|
@@ -4,6 +4,12 @@ Approval saves the application's verified `auth_time`, `acr` and `amr`. Saving a
|
|
|
4
4
|
value does not by itself select it for disclosure in a newly accepted request.
|
|
5
5
|
Initial and refreshed ID Tokens use the same stored selection:
|
|
6
6
|
|
|
7
|
+
Refresh sources additionally retain the actual initial issuer, subject, audience
|
|
8
|
+
and disclosed authentication time in a versioned JSON object. Refreshed ID Tokens
|
|
9
|
+
retain that original context, use a new issuance time and omit nonce. Legacy
|
|
10
|
+
refresh JSON cannot prove the initial values and is rejected. See the
|
|
11
|
+
[refresh migration](refresh-tokens.md#original-id-token-binding-and-migration).
|
|
12
|
+
|
|
7
13
|
- `require_auth_time: true` or configured `default_max_age` client metadata selects
|
|
8
14
|
`auth_time` at request acceptance. This is output selection, not freshness enforcement.
|
|
9
15
|
- A nonempty `acr_values` request selects `acr`. If omitted or empty, registered
|
|
@@ -21,9 +27,13 @@ ciba_authentication_claims_by_scope "openid" => %w[auth_time acr amr]
|
|
|
21
27
|
The default mapping is empty. Only the three names above are considered. Scope
|
|
22
28
|
selection uses the scopes authorized for the current issuance, including refresh
|
|
23
29
|
narrowing. It is application configuration, not a client-supplied mapping.
|
|
30
|
+
For refreshed `auth_time`, the initial disclosure is authoritative: an initially
|
|
31
|
+
absent claim stays absent, and a disclosed claim keeps its original value even
|
|
32
|
+
if the scope mapping is subsequently changed.
|
|
24
33
|
Client metadata and explicit request selection are saved at acceptance; replacing
|
|
25
34
|
client metadata later does not reinterpret an accepted request or refresh source.
|
|
26
|
-
An accepted `require_auth_time: true
|
|
35
|
+
An accepted `require_auth_time: true`, selected `max_age` (including zero), or
|
|
36
|
+
`claims.id_token.auth_time.essential: true` is saved as the `auth_time_required` marker
|
|
27
37
|
in the existing authentication selection. Approval requires a verified
|
|
28
38
|
`auth_time` and rejects a missing value before changing the pending request.
|
|
29
39
|
Issuance and refresh reject an incomplete required context with `invalid_grant`
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# CIBA Core conformance boundaries
|
|
2
|
+
|
|
3
|
+
This local change targets [CIBA Core 1.0 final](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0-final.html), not every optional OpenID/OAuth profile. Supported release scope is poll plus documented opt-in ping, signed requests, identity hints and user codes. Push is not implemented or advertised. Pairwise and CIBA dynamic registration remain experimental and disabled by default.
|
|
4
|
+
|
|
5
|
+
[Requirement ledger](ciba-core-requirements.json) separates mandatory, prohibited, recommended, conditional and optional requirements, as well as gem/upstream/application/client/deployment responsibilities. Its 159 inherited CIBA decomposition rows plus five OIDC conditional rows are an authored inventory, not an official denominator. Historical evidence is not promoted to a current conformance pass. Grouped tests support specific boundaries, not an exhaustive proof for each row.
|
|
6
|
+
|
|
7
|
+
## Design and changes
|
|
8
|
+
|
|
9
|
+
The backend additionally enforces requested public/pairwise subjects and essential ACR value conditions at approval, issuance and refresh. Issued projections retain ordinary value/values filters for both targets. Generic essential claim absence is allowed; voluntary ACR has its specific semantics. Mandatory nonce and final signed UserInfo claims cannot bypass filtering, and pairwise UserInfo retains its issuance subject. Positive integral JSON numbers are accepted for signed requested_expiry. See claims and operations for legacy-token migration limits.
|
|
10
|
+
|
|
11
|
+
1. Capture request receipt at entry to the backchannel POST handler, before client authentication, signed-request verification or hint resolution. Store created_at at that point and expires_at = created_at + chosen lifetime. This is the earliest protocol-handler timestamp, not a guarantee about time spent in a reverse proxy or Rack body parsing before the handler.
|
|
12
|
+
2. Keep expires_in equal to the selected positive lifetime, measured from that receipt point as specified in CIBA section7.3; do not reinterpret it as seconds remaining at response.
|
|
13
|
+
3. Do not add a new Backchannel error for slow processing. Section7.3 does not independently forbid acknowledgement after processing exhausts the lifetime; polling an expired source returns expired_token. The token endpoint503 provision is not a Backchannel authentication error permission. Applications must promptly enqueue AD work and clean up/reconcile abandoned requests; fixed expires_in is not guaranteed remaining lifetime on receipt of the response.
|
|
14
|
+
4. Save auth_time_required when client require_auth_time=true, max_age is selected, or claims.id_token.auth_time.essential=true. Reuse the existing approval/issuance/refresh guards. Null or essential=false requests remain voluntary. No fabricated timestamps or schema migration.
|
|
15
|
+
5. Continue delegating actual authentication and freshness to the application. max_age=0 requires fresh authentication; merely supplying an arbitrary timestamp is insufficient. The gem checks required context presence and requested essential ACR values; the truth of authentication and attribute evidence remains an application obligation.
|
|
16
|
+
|
|
17
|
+
Refresh stores the actual initial iss/sub/aud and disclosed auth_time in versioned
|
|
18
|
+
existing JSON storage, preserves the context through rotation, rejects old or
|
|
19
|
+
invalid contexts before mutation, and omits refresh nonce. See the
|
|
20
|
+
[migration contract](refresh-tokens.md#original-id-token-binding-and-migration).
|
|
21
|
+
This fifth conditional OIDC requirement is in the backend ledger
|
|
22
|
+
(164 total authored rows) even though
|
|
23
|
+
refresh/pairwise remain experimental. The latest local validation does not
|
|
24
|
+
establish the official suite or every optional combination.
|
|
25
|
+
|
|
26
|
+
## Requirement ownership and acceptance gates
|
|
27
|
+
|
|
28
|
+
| Area | Gem / upstream boundary | Application or deployment gate |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| Discovery and registration, section4 | Advertise only enabled modes/algorithms/flags; require configured confidential client, delivery mode and CIBA grant. Upstream owns ordinary OP metadata/authentication. | Stable HTTPS issuer/endpoints, approved client/key ownership. Experimental pairwise/DCR needs separate profile verification. |
|
|
31
|
+
| Request, sections7–7.2 | TLS/form/UTF8, one hint, openid, size/type checks, registered authentication, JWT verification, signed inner inputs, unknown parameters ignored. | Correct hint-to-account/tenant mapping; login_hint_token signature/issuer/profile validation; required hint retention policy. |
|
|
32
|
+
| Signed request, section7.1.1 | Asymmetric registered keys, mandatory claims, issuer/audience/date/type verification; client-auth JWT replay ledger is separate. | Client supplies unique request jti and no unsigned authentication parameters. The gem does not consume request-jti and does not claim request replay prevention. It creates new requests requiring new consent. |
|
|
33
|
+
| Binding/user code, sections7.1–7.1.2 | Optional metadata and bounded inputs, required validation callbacks, errors. | Display quality, user-code enrollment/change/password distinction, code verification/rate limit. Client never stores user code. |
|
|
34
|
+
| Acknowledgement, section7.3 | 256bit opaque ID, allowed alphabet, receipt-based lifetime, positive interval; processing never extends the deadline. | Proxy/queue delays before the handler, prompt dispatch and client expiry tracking. |
|
|
35
|
+
| Authentication/consent, section8 | Completion account/client/scope binding, trusted context types, transactional decisions. | Real AD authentication, consent UI, CSRF/authority checks, achieved acr, freshness and business policy before approval. |
|
|
36
|
+
| Token, sections10–11 | Client-bound source, current consent/openid/account/client checks, one-time issuance, locks/CAS/rollback, pending/slow_down+5/expiry/denied errors. | Same database, supported schema; RP does not overlap polls and obeys intervals. Outer transactions/overrides need integration tests. |
|
|
37
|
+
| Ping, sections9/10.2 | HTTPS POST after commit, auth_req_id-only body, Bearer notification token,200/204, no redirects; SSRF/DNS/TLS/size/time boundaries. | Client notification-token entropy and callback validation; endpoint administrative ownership; durable delivery/retry and logs. |
|
|
38
|
+
| ID Token, section10.1.1 / OIDC Core2 | Upstream core fields and signature plus RP audience, saved authcontext/nonce, required auth_time guard. | Valid signing setup, real authcontext, freshness and business policy. Push-only hash/id claims are not applicable. |
|
|
39
|
+
| Errors, sections11/13 | HTTP/JSON envelope, safe descriptions, client-auth401, policy403, token-state errors. | Custom callback error handling and log confidentiality. Optional error_uri absent; its output-specific restriction is not applicable. |
|
|
40
|
+
| Recommendations, sections14/15 | Optional hint validation and privacy-preserving persistence; protected outbound transport. | Reasonable expired-hint retention, notification ownership, privacy identifiers/retention, user matching and anti-abuse controls. |
|
|
41
|
+
|
|
42
|
+
The gem cannot certify an arbitrary consuming OP: several mandatory requirements explicitly need application authentication/consent and trusted deployment configuration. Referenced RFCs and OIDC Core bring their own requirements. DPoP/mTLS/resources/RAR tests cover documented extension boundaries, not all profile combinations.
|
|
43
|
+
|
|
44
|
+
## Regression evidence
|
|
45
|
+
|
|
46
|
+
The initial exploratory regression run on unmodified0.1.0 had5tests/4failures/0errors. One expectation rejected an expired acknowledgement; independent specification review showed that expectation added an unsupported503 and was removed. The retained regressions cover late receipt timestamp, approval without requested required auth_time, and issuance after authentication context was removed. The delayed-dispatch test checks that polling returns expired_token without extending the receipt-based deadline. Final test results are recorded in the delivery report.
|
|
47
|
+
|
|
48
|
+
The existing requirements timing test now expects the Core receipt-based policy rather than equality with node-oidc-provider9.12.2. The reference fixture still describes the reference's old measured behavior; that difference is deliberate and is not a conformance oracle.
|
|
49
|
+
|
|
50
|
+
The complete suite, installed-package verification and applicable platform coverage are reported with actual command results in the delivery report. Older checked-in validation logs apply to their recorded source snapshots only.
|
|
51
|
+
|
|
52
|
+
## Official conformance suite
|
|
53
|
+
|
|
54
|
+
The [OpenID Foundation suite](https://gitlab.com/openid/conformance-suite) is available for local or hosted testing; [testing is free](https://openid.net/certification/about-conformance-suite/), formal certification has a separate submission process. The published [CIBA OP testing instructions](https://openid.net/certification/fapi_ciba_op_testing/) address FAPI-CIBA: that profile imposes additional requirements and is not an automatic Core-only test result.
|
|
55
|
+
|
|
56
|
+
An executable OP deployment, suitable registered clients/keys, reachable TLS endpoints, and an AD flow capable of completing the suite's user scenarios are needed. Hosted testing additionally requires the suite to reach the OP; local suite setup depends on its Java/database/container runtime. No hosted account, client authorization configuration, public endpoint, credentials or certification submission is created by this change. No official suite run or certification is claimed.
|
|
57
|
+
|
|
58
|
+
Next acceptance gates: run a pinned official plan against an isolated complete OP/AD deployment, document any profile-specific extras, retain machine-readable results, and audit each enabled optional combination plus all application contracts. Full Core conformance remains unproven until those gates and the ledger's open responsibilities are verified.
|