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,182 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rodauth
|
|
4
|
+
module CibaSupport
|
|
5
|
+
# Explicit migration helper. Never called automatically on application startup.
|
|
6
|
+
module Schema
|
|
7
|
+
# Preserve the signed token's original subject across client sector changes.
|
|
8
|
+
def self.add_pairwise_subject(db, table: :oauth_grants, column: :ciba_subject)
|
|
9
|
+
db.alter_table(table) { add_column column, String, size: 255 }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Lookup only: management credentials remain verified against upstream's
|
|
13
|
+
# password hash. Existing clients have no digest until credentials are issued.
|
|
14
|
+
def self.add_registration_token_digest(db, table: :oauth_applications, column: :ciba_registration_token_digest)
|
|
15
|
+
db.alter_table(table) do
|
|
16
|
+
add_column column, String, size: 64
|
|
17
|
+
add_index column, unique: true
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.add_request_context(db, table: :ciba_requests, column: :request_context)
|
|
22
|
+
db.alter_table(table) { add_column column, String, text: true }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# NULL preserves the output contract of already accepted requests/sources.
|
|
26
|
+
def self.add_authentication_claims(db, table: :ciba_requests, column: :authentication_claims)
|
|
27
|
+
db.alter_table(table) { add_column column, String, text: true }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.add_request_max_age(db, table: :ciba_requests, column: :max_age)
|
|
31
|
+
db.alter_table(table) { add_column column, :bigint }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# NULL retains access_denied for previously denied requests.
|
|
35
|
+
def self.add_completion_error(db, table: :ciba_requests, column: :completion_error)
|
|
36
|
+
db.alter_table(table) { add_column column, String, text: true }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Optional refresh sources are independent of collected access tokens and
|
|
40
|
+
# request cleanup. Consumed digests remain until expiry for replay detection.
|
|
41
|
+
# Creating storage does not enable protocol support.
|
|
42
|
+
def self.create_refresh_tokens(db, table: :ciba_refresh_tokens, grants_table: :ciba_grants,
|
|
43
|
+
accounts_table: :accounts, account_key: :id, account_type: Integer,
|
|
44
|
+
applications_table: :oauth_applications, application_key: :id,
|
|
45
|
+
application_type: Integer)
|
|
46
|
+
db.create_table(table) do
|
|
47
|
+
primary_key :id
|
|
48
|
+
String :token_digest, size: 64, unique: true, null: false
|
|
49
|
+
foreign_key :grant_id, grants_table, null: false, on_delete: :cascade
|
|
50
|
+
foreign_key :account_id, accounts_table, key: account_key, type: account_type, null: false, on_delete: :cascade
|
|
51
|
+
foreign_key :oauth_application_id, applications_table, key: application_key, type: application_type, null: false, on_delete: :cascade
|
|
52
|
+
String :scopes, text: true, null: false
|
|
53
|
+
%i[created_at issued_at expires_at].each { |name| Bignum name, null: false }
|
|
54
|
+
Bignum :auth_time
|
|
55
|
+
Bignum :consumed_at
|
|
56
|
+
Integer :lock_version, null: false, default: 0
|
|
57
|
+
Integer :rotations, null: false, default: 0
|
|
58
|
+
%i[acr amr nonce authentication_claims requested_claims requested_resources requested_authorization_details].each do |name|
|
|
59
|
+
String name, text: true
|
|
60
|
+
end
|
|
61
|
+
index :grant_id
|
|
62
|
+
index :expires_at
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.add_ping(db, applications_table: :oauth_applications,
|
|
67
|
+
endpoint_column: :backchannel_client_notification_endpoint,
|
|
68
|
+
table: :ciba_ping_deliveries, requests_table: :ciba_requests, request_key: :id)
|
|
69
|
+
db.alter_table(applications_table) { add_column endpoint_column, String, text: true }
|
|
70
|
+
db.create_table(table) do
|
|
71
|
+
foreign_key :request_id, requests_table, key: request_key, primary_key: true, null: false, on_delete: :cascade
|
|
72
|
+
String :auth_req_id, null: false
|
|
73
|
+
String :notification_token, text: true, null: false
|
|
74
|
+
String :endpoint, text: true, null: false
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.add_user_code(db, table: :oauth_applications, column: :backchannel_user_code_parameter)
|
|
79
|
+
db.alter_table(table) { add_column column, TrueClass }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.add_signed_requests(db, table: :oauth_applications,
|
|
83
|
+
column: :backchannel_authentication_request_signing_alg)
|
|
84
|
+
db.alter_table(table) { add_column column, String, size: 16 }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Storage foundation for optional RAR; no backfill implies no permission.
|
|
88
|
+
# Protocol activation is separate from this explicit migration.
|
|
89
|
+
def self.add_authorization_details(db, requests_table: :ciba_requests,
|
|
90
|
+
request_column: :requested_authorization_details,
|
|
91
|
+
grants_table: :ciba_grants, tokens_table: :oauth_grants,
|
|
92
|
+
applications_table: :oauth_applications)
|
|
93
|
+
db.alter_table(requests_table) { add_column request_column, String, text: true }
|
|
94
|
+
db.alter_table(grants_table) { add_column :authorization_details, String, text: true }
|
|
95
|
+
db.alter_table(tokens_table) { add_column :ciba_authorization_details, String, text: true }
|
|
96
|
+
db.alter_table(applications_table) { add_column :authorization_details_types, String, text: true }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Optional resource indicators. Activate only after all workers are upgraded.
|
|
100
|
+
def self.add_resources(db, requests_table: :ciba_requests, request_column: :requested_resources,
|
|
101
|
+
grants_table: :ciba_grants, tokens_table: :oauth_grants)
|
|
102
|
+
db.alter_table(requests_table) { add_column request_column, String, text: true }
|
|
103
|
+
db.alter_table(grants_table) { add_column :resources, String, text: true }
|
|
104
|
+
db.alter_table(tokens_table) do
|
|
105
|
+
add_column :ciba_resource, String, text: true
|
|
106
|
+
add_column :ciba_resource_audience, String, text: true
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Optional explicit-claims capability; run once after the base schema.
|
|
111
|
+
def self.add_claims(db, requests_table: :ciba_requests, request_column: :requested_claims,
|
|
112
|
+
grants_table: :ciba_grants, tokens_table: :oauth_grants)
|
|
113
|
+
db.alter_table(requests_table) { add_column request_column, String, text: true }
|
|
114
|
+
db.alter_table(grants_table) { add_column :claims, String, text: true }
|
|
115
|
+
db.alter_table(tokens_table) { add_column :ciba_claims, String, text: true }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Forward migration for development schemas created before nonce support.
|
|
119
|
+
def self.add_request_nonce(db, table: :ciba_requests, column: :nonce)
|
|
120
|
+
db.alter_table(table) { add_column column, String, text: true }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def self.add_client_metadata(db, table: :oauth_applications, column: :backchannel_token_delivery_mode)
|
|
124
|
+
db.alter_table(table) { add_column column, String, size: 8 }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Shared replay ledger for JWT authentication by CIBA-capable clients.
|
|
128
|
+
def self.create_client_assertions(db, table: :ciba_client_assertions)
|
|
129
|
+
db.create_table(table) do
|
|
130
|
+
String :digest, size: 64, primary_key: true, null: false
|
|
131
|
+
Bignum :expires_at, null: false
|
|
132
|
+
index :expires_at
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Consent is separate from upstream OAuth grant rows, which also store tokens.
|
|
137
|
+
def self.create_grants(db, table: :ciba_grants, accounts_table: :accounts,
|
|
138
|
+
account_key: :id, account_type: Integer,
|
|
139
|
+
applications_table: :oauth_applications, application_key: :id,
|
|
140
|
+
application_type: Integer)
|
|
141
|
+
db.create_table(table) do
|
|
142
|
+
primary_key :id
|
|
143
|
+
foreign_key :account_id, accounts_table, key: account_key, type: account_type, null: false, on_delete: :cascade
|
|
144
|
+
foreign_key :oauth_application_id, applications_table, key: application_key, type: application_type, null: false, on_delete: :cascade
|
|
145
|
+
String :scopes, text: true, null: false
|
|
146
|
+
Bignum :created_at, null: false
|
|
147
|
+
Bignum :expires_at
|
|
148
|
+
Bignum :revoked_at
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def self.add_grant_reference(db, table: :oauth_grants, column: :ciba_grant_id, grants_table: :ciba_grants)
|
|
153
|
+
db.alter_table(table) { add_foreign_key column, grants_table, index: true, on_delete: :cascade }
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def self.create(db, table: :ciba_requests, columns: {}, accounts_table: :accounts,
|
|
157
|
+
account_key: :id, account_type: Integer, applications_table: :oauth_applications,
|
|
158
|
+
application_key: :id, application_type: Integer, grants_table: :ciba_grants)
|
|
159
|
+
col = ->(name) { columns.fetch(name, name) }
|
|
160
|
+
db.create_table(table) do
|
|
161
|
+
primary_key col[:id]
|
|
162
|
+
String col[:auth_req_id_digest], size: 64, unique: true, null: false
|
|
163
|
+
foreign_key col[:account_id], accounts_table, key: account_key, type: account_type, null: false, on_delete: :cascade
|
|
164
|
+
foreign_key col[:oauth_application_id], applications_table, key: application_key, type: application_type, null: false, on_delete: :cascade
|
|
165
|
+
foreign_key col[:grant_id], grants_table, on_delete: :cascade
|
|
166
|
+
String col[:scopes], text: true, null: false
|
|
167
|
+
String col[:status], size: 16, null: false, default: "pending"
|
|
168
|
+
Integer col[:lock_version], null: false, default: 0
|
|
169
|
+
%i[created_at expires_at].each { |name| Bignum col[name], null: false }
|
|
170
|
+
%i[last_polled_at completed_at consumed_at auth_time max_age].each { |name| Bignum col[name] }
|
|
171
|
+
Bignum col[:interval], null: false, default: 5
|
|
172
|
+
%i[binding_message acr_values acr amr nonce authentication_claims completion_error].each { |name| String col[name], text: true }
|
|
173
|
+
index [col[:status], col[:id]]
|
|
174
|
+
index col[:expires_at]
|
|
175
|
+
check(Sequel[col[:interval]] > 0)
|
|
176
|
+
check(Sequel[col[:lock_version]] >= 0)
|
|
177
|
+
check(Sequel[col[:status]] => %w[pending approved denied consumed])
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
data/lib/rodauth/ciba.rb
ADDED