standard_id 0.51.0 → 0.51.2
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 +12 -0
- data/README.md +1 -1
- data/lib/standard_id/config/callable_validator.rb +7 -4
- data/lib/standard_id/oauth/direct_mint_flow.rb +17 -1
- data/lib/standard_id/oauth/token_grant_flow.rb +6 -1
- data/lib/standard_id/token_minter.rb +11 -2
- data/lib/standard_id/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 18c7955706f1fbe444d4bd7db052f615acfb01d7310f1a9c7088717017b864c5
|
|
4
|
+
data.tar.gz: 65435d9e1d57e2bfcbd479b5ecd81353d2961f831ad3f53db911bf830d20d89e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec9734a8609d74823eb5b5a1b7b395d28f11c38268a19ff305618145777543ac13a5671c54f66cf2ecae5f68a9924e54dfeed495726dbc554f591d7f52f07078
|
|
7
|
+
data.tar.gz: 8616544546bd160e81490b665f40e5d4b64b03b2a612128efe896c4aacea446dbd240a8518e9d9c9a1e8bed1ef491421a406deb22ccfc52bd73f03fc6dab6a8e
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.51.2] - 2026-10-21
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`claim_resolvers` and `custom_claims` may declare `profile:`.** The grant flows (and `TokenMinter`) have always passed the bound profile as `profile:` in the resolver context, but the boot-time `Config::CallableValidator` rejected the keyword, so a resolver could not actually use it — including the explicit `TokenMinter.mint(profile:)` added in 0.51.0.
|
|
15
|
+
|
|
16
|
+
## [0.51.1] - 2026-10-20
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **`TokenMinter.mint(requested_scopes:)`**: what `OAUTH_TOKEN_ISSUED` reports as `requested_scopes` for a direct mint, for a caller whose audit trail records the permissions it granted (carried in a host claim such as `permissions`) rather than the OAuth scope words. Default unchanged (the scope words); an Array or space-delimited String; the token is never affected. Lets sidekick-web move its staff/test mint sites onto `TokenMinter` without its `auth.oauth.token.issued` audit rows losing the grant (rarebit-sre#478).
|
|
21
|
+
|
|
10
22
|
## [0.51.0] - 2026-10-20
|
|
11
23
|
|
|
12
24
|
### Added
|
data/README.md
CHANGED
|
@@ -925,7 +925,7 @@ Every StandardId event automatically carries tracing metadata (`event_id`, `time
|
|
|
925
925
|
| | `oauth.authorization.granted` | `authorization_code`, `client_id`, `account`, `scope` | After an authorization code is created |
|
|
926
926
|
| | `oauth.authorization.denied` | `client_id`, `account`, `reason` | When a user denies authorization |
|
|
927
927
|
| | `oauth.token.issuing` | `grant_type`, `client_id`, `account`, `scope` | Before generating access/refresh tokens |
|
|
928
|
-
| | `oauth.token.issued` | `access_token_id`, `grant_type`, `client_id`, `account`, `expires_in` | After tokens are generated |
|
|
928
|
+
| | `oauth.token.issued` | `access_token_id`, `grant_type`, `client_id`, `account`, `expires_in` | After tokens are generated. For a `TokenMinter` mint, `requested_scopes` may be caller-supplied (`requested_scopes:`) rather than the token's `scope` |
|
|
929
929
|
| | `oauth.token.refreshed` | `old_token_id`, `new_token_id`, `client_id`, `account` | After a refresh token is redeemed |
|
|
930
930
|
| | `oauth.code.consumed` | `authorization_code`, `client_id`, `account` | After an authorization code is exchanged |
|
|
931
931
|
| | `oauth.client.registered` | `client_application`, `client_id`, `client_name`, `client_type`, `redirect_uris`, `scope`, `owner_type`, `owner_id`, `client_metadata` | After a dynamic registration (RFC 7591) commits. Never fails the registration: a raising subscriber is logged |
|
|
@@ -42,9 +42,9 @@ module StandardId
|
|
|
42
42
|
keywords: %i[account auth_method provider request flow]
|
|
43
43
|
},
|
|
44
44
|
"oauth.custom_claims" => {
|
|
45
|
-
signature: "(account:, client:, request:, audience:)",
|
|
45
|
+
signature: "(account:, client:, request:, audience:, profile:)",
|
|
46
46
|
kind: :keyword,
|
|
47
|
-
keywords: %i[account client request audience]
|
|
47
|
+
keywords: %i[account client request audience profile]
|
|
48
48
|
},
|
|
49
49
|
"oauth.audience_resolver" => {
|
|
50
50
|
signature: "(client:, audience:, resource:, request:)",
|
|
@@ -73,8 +73,11 @@ module StandardId
|
|
|
73
73
|
}
|
|
74
74
|
}.freeze
|
|
75
75
|
|
|
76
|
-
# Keys available to claim_resolvers callables at invocation time
|
|
77
|
-
|
|
76
|
+
# Keys available to claim_resolvers callables at invocation time
|
|
77
|
+
# (TokenGrantFlow#claim_resolvers_context). `profile` is the profile the
|
|
78
|
+
# audience binding resolved, or the one passed to TokenMinter; nil when
|
|
79
|
+
# the audience has no binding.
|
|
80
|
+
CLAIM_RESOLVER_KEYWORDS = %i[client account request audience profile].freeze
|
|
78
81
|
|
|
79
82
|
class << self
|
|
80
83
|
def validate!(config = StandardId.config)
|
|
@@ -16,9 +16,11 @@ module StandardId
|
|
|
16
16
|
AUDIT_ERROR_MODES = %i[raise report].freeze
|
|
17
17
|
|
|
18
18
|
def initialize(account:, audience:, grant_type:, scopes: nil, client_id: nil, claims: {}, expires_in: nil,
|
|
19
|
-
refresh_token: false, request: nil, auth_method: nil, auth_provider: nil, profile: nil, audit_errors: :raise
|
|
19
|
+
refresh_token: false, request: nil, auth_method: nil, auth_provider: nil, profile: nil, audit_errors: :raise,
|
|
20
|
+
requested_scopes: nil)
|
|
20
21
|
super({ audience: audience }, request || NullRequest.new)
|
|
21
22
|
@profile = profile
|
|
23
|
+
@audit_requested_scopes = normalize_audit_scopes(requested_scopes)
|
|
22
24
|
@audit_errors = audit_errors.to_s.to_sym
|
|
23
25
|
unless AUDIT_ERROR_MODES.include?(@audit_errors)
|
|
24
26
|
raise ArgumentError, "audit_errors must be one of #{AUDIT_ERROR_MODES.join(', ')} (got #{audit_errors.inspect})"
|
|
@@ -121,6 +123,12 @@ module StandardId
|
|
|
121
123
|
end
|
|
122
124
|
end
|
|
123
125
|
|
|
126
|
+
# A direct mint's caller may record what it actually granted (a
|
|
127
|
+
# permission set carried in a host claim) rather than the scope words.
|
|
128
|
+
def issued_event_requested_scopes
|
|
129
|
+
@audit_requested_scopes.nil? ? super : @audit_requested_scopes
|
|
130
|
+
end
|
|
131
|
+
|
|
124
132
|
# No new authentication happened here: the host vouches for the account.
|
|
125
133
|
def login_method_policy_context
|
|
126
134
|
nil
|
|
@@ -181,6 +189,14 @@ module StandardId
|
|
|
181
189
|
super
|
|
182
190
|
end
|
|
183
191
|
|
|
192
|
+
# nil keeps the scope words; otherwise an Array or a space-delimited
|
|
193
|
+
# String, split like `scopes`.
|
|
194
|
+
def normalize_audit_scopes(scopes)
|
|
195
|
+
return nil if scopes.nil?
|
|
196
|
+
|
|
197
|
+
Array(scopes).flat_map { |s| s.to_s.split(/\s+/) }.reject(&:blank?).uniq
|
|
198
|
+
end
|
|
199
|
+
|
|
184
200
|
def normalize_scopes(scopes)
|
|
185
201
|
list = Array(scopes).flat_map { |s| s.to_s.split(/\s+/) }.reject(&:blank?).uniq
|
|
186
202
|
list.empty? ? nil : list.join(" ")
|
|
@@ -487,9 +487,14 @@ module StandardId
|
|
|
487
487
|
profile_id: @resolved_profile&.id,
|
|
488
488
|
audience: audience,
|
|
489
489
|
jti: @issued_jti,
|
|
490
|
-
requested_scopes:
|
|
490
|
+
requested_scopes: issued_event_requested_scopes
|
|
491
491
|
)
|
|
492
492
|
end
|
|
493
|
+
|
|
494
|
+
# What OAUTH_TOKEN_ISSUED reports as `requested_scopes`: the scope words.
|
|
495
|
+
def issued_event_requested_scopes
|
|
496
|
+
current_scopes
|
|
497
|
+
end
|
|
493
498
|
end
|
|
494
499
|
end
|
|
495
500
|
end
|
|
@@ -71,13 +71,22 @@ module StandardId
|
|
|
71
71
|
# caller whose contract is that an audit failure never costs it the
|
|
72
72
|
# credential. Report it to your error tracker: the gem only logs it.
|
|
73
73
|
# OAUTH_TOKEN_ISSUING (the status / locking guards) always raises.
|
|
74
|
+
# @param requested_scopes [Array<String>, String, nil] what OAUTH_TOKEN_ISSUED
|
|
75
|
+
# reports as `requested_scopes`, for a caller whose audit trail records
|
|
76
|
+
# the permissions it granted (carried in a host claim) rather than the
|
|
77
|
+
# OAuth scope words; default (nil) is the scope words, like every grant.
|
|
78
|
+
# A String is split on whitespace. Changes the event only, never the
|
|
79
|
+
# token — so for a direct mint, the event's requested_scopes may be
|
|
80
|
+
# caller-supplied rather than what the token's `scope` claim says.
|
|
74
81
|
# @return [Result]
|
|
75
82
|
def mint(account:, audience:, grant_type:, scopes: nil, client_id: nil, claims: {}, expires_in: nil,
|
|
76
|
-
refresh_token: false, request: nil, auth_method: nil, auth_provider: nil, profile: nil, audit_errors: :raise
|
|
83
|
+
refresh_token: false, request: nil, auth_method: nil, auth_provider: nil, profile: nil, audit_errors: :raise,
|
|
84
|
+
requested_scopes: nil)
|
|
77
85
|
flow = StandardId::Oauth::DirectMintFlow.new(
|
|
78
86
|
account: account, audience: audience, grant_type: grant_type, scopes: scopes, client_id: client_id,
|
|
79
87
|
claims: claims, expires_in: expires_in, refresh_token: refresh_token, request: request,
|
|
80
|
-
auth_method: auth_method, auth_provider: auth_provider, profile: profile, audit_errors: audit_errors
|
|
88
|
+
auth_method: auth_method, auth_provider: auth_provider, profile: profile, audit_errors: audit_errors,
|
|
89
|
+
requested_scopes: requested_scopes
|
|
81
90
|
)
|
|
82
91
|
response = flow.execute
|
|
83
92
|
Result.new(
|
data/lib/standard_id/version.rb
CHANGED