standard_id 0.40.0 → 0.41.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 +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +32 -0
- data/app/models/standard_id/client_application.rb +7 -2
- data/db/migrate/20260915000000_remove_refresh_token_lifetime_from_standard_id_client_applications.rb +23 -0
- data/lib/standard_id/config/schema.rb +33 -0
- data/lib/standard_id/oauth/audience_scope_resolver.rb +179 -0
- data/lib/standard_id/testing/factories/oauth.rb +0 -1
- data/lib/standard_id/version.rb +1 -1
- data/lib/standard_id.rb +1 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8cbe67c8d05fe6c7daeeb4d4a44562507e1d5104c32eeba94b701e98e6afe3ea
|
|
4
|
+
data.tar.gz: 8f9cb74cf37bb063c66547c7f563005f870a5a86509fe8f89b0bb57893f0ed82
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e8110399efd07b7b0455f2b8a5ee2c93acfb7dd9bb3847c1a7b43a78aec874b1709aebc05d7be37877007b81f19946322496745793da0418757ecd3de0349121
|
|
7
|
+
data.tar.gz: '08736fdbf31a259270c02c232f3a8e163c1dc80ca7f5fd3a4dba2168adb812aa91307bf662b7766bc0078b532bf549be64998342cf90f4e5c54048d31f2becd8'
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.41.0] - 2026-09-15
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Per-audience scope vocabulary hook.** An app can now declare, per audience, the set of scopes a client targeting that audience may request and be granted — the mechanism sidekick-web needs to express its per-audience MCP scope vocabularies (`mcp`, `mcp:read`, `mcp:eval:run`, …) through the gem instead of hard-coding a single flat `scopes_supported` list shared by every audience. This is the scope-side counterpart to the existing `audience_profile_types` binding: that binds an audience to a required profile, this binds an audience to a grantable scope vocabulary, and the two are configured and read the same way on purpose.
|
|
15
|
+
|
|
16
|
+
`c.oauth.audience_scopes` is a static `audience => Array<String>` map; `c.oauth.audience_scope_resolver` is an optional callable `->(audience:, client:, configured_scopes:) { … }` for apps that compute the vocabulary dynamically (per-client entitlements), filtered by arity like every other gem callable and returning `nil` to fall back to the static map. `StandardId::Oauth::AudienceScopeResolver` reads them and exposes `scopes_for`, `configured_for?`, `permits?`, `filter` (narrow a grant to the vocabulary), `disallowed`, and `assert!` (fail-closed, raising `InvalidScopeError` / RFC 6749 `invalid_scope`, naming only the client's offending scopes so the endpoint is not a vocabulary-enumeration oracle).
|
|
17
|
+
|
|
18
|
+
An **unconfigured** audience fails **open** everywhere — `filter`/`assert!` pass requested scopes through unchanged — exactly like `audience_profile_types` skips its check when unmapped, so an app that does not model per-audience vocabularies sees no behaviour change. The resolver ships ahead of any mint-/registration-time wiring so the hook API can be settled in review first, mirroring how `AudienceProfileResolver#resolve!` shipped before its strict path was wired.
|
|
19
|
+
|
|
20
|
+
### Removed
|
|
21
|
+
|
|
22
|
+
- **The per-client `refresh_token_lifetime` column is dropped** (`standard_id_client_applications`), resolving the #765 asymmetry. It was never honoured: `TokenLifetimeResolver.refresh_token_lifetime` resolves the refresh-token lifetime **globally** from `oauth.refresh_token_lifetime` and has no per-client branch, so the column advertised a knob that did nothing. Refresh-token lifetime is a global policy by design — a client's re-authorization cadence is governed by **revocation**, not by a per-client lifetime (see the 0.39.x linked-session reasoning: "Revocation is the property the estate wants; lifetime remains `refresh_token_lifetime`'s job, which already exists and is already configured per host"). Access- and authorization-code lifetimes remain per-client.
|
|
23
|
+
|
|
24
|
+
**Migration:** consumers pick up `20260915000000_remove_refresh_token_lifetime_from_standard_id_client_applications` (idempotent, reversible). The global `oauth.refresh_token_lifetime` config is unchanged and continues to govern refresh-token lifetime for every client.
|
|
25
|
+
|
|
10
26
|
## [0.40.0] - 2026-09-05
|
|
11
27
|
|
|
12
28
|
### Fixed
|
data/README.md
CHANGED
|
@@ -235,6 +235,38 @@ end
|
|
|
235
235
|
|
|
236
236
|
Resolvers receive keyword arguments with the context containing `client`, `account`, and `request`, so you can reference only what you need. This lets you, for example, pull organization info off the client application or decorate claims with account attributes.
|
|
237
237
|
|
|
238
|
+
### Per-Audience Scope Vocabulary
|
|
239
|
+
|
|
240
|
+
Declare, per audience, the set of scopes a client targeting that audience may request and be granted. This lets each audience expose its own MCP scope vocabulary rather than sharing one flat `scopes_supported` list. It is the scope-side counterpart to the `audience_profile_types` binding: that binds an audience to a required profile, this binds it to a grantable scope vocabulary.
|
|
241
|
+
|
|
242
|
+
```ruby
|
|
243
|
+
StandardId.configure do |config|
|
|
244
|
+
config.oauth.audience_scopes = {
|
|
245
|
+
"harness" => %w[mcp mcp:read mcp:eval:run mcp:prompt:write],
|
|
246
|
+
"companion_kit" => %w[mcp mcp:read],
|
|
247
|
+
"admin_kit" => %w[mcp mcp:read mcp:admin]
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
# Optional: compute a vocabulary dynamically (e.g. per-client entitlements).
|
|
251
|
+
# Return an Array<String>, or nil to fall back to the static map above.
|
|
252
|
+
config.oauth.audience_scope_resolver = ->(audience:, client:, configured_scopes:) {
|
|
253
|
+
Entitlements.mcp_scopes_for(client, audience) || configured_scopes
|
|
254
|
+
}
|
|
255
|
+
end
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
`StandardId::Oauth::AudienceScopeResolver` reads this config:
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
R = StandardId::Oauth::AudienceScopeResolver
|
|
262
|
+
R.scopes_for(audience: "companion_kit") # => ["mcp", "mcp:read"]
|
|
263
|
+
R.permits?(scope: "mcp:admin", audience: "companion_kit") # => false
|
|
264
|
+
R.filter(requested: %w[mcp mcp:admin], audience: "companion_kit") # => ["mcp"] (narrows, never raises)
|
|
265
|
+
R.assert!(requested: %w[mcp mcp:admin], audience: "companion_kit") # => raises InvalidScopeError
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
An **unconfigured** audience fails **open** — `filter` and `assert!` pass the requested scopes through unchanged — so an app that does not model per-audience vocabularies sees no behaviour change; narrowing only bites once an audience has an explicit vocabulary.
|
|
269
|
+
|
|
238
270
|
### Social Login Setup
|
|
239
271
|
|
|
240
272
|
The `social.google_*` and `social.apple_*` fields are declared by the **provider
|
|
@@ -19,8 +19,13 @@ module StandardId
|
|
|
19
19
|
validates :scopes, presence: true
|
|
20
20
|
validates :code_challenge_methods, presence: true, if: :require_pkce?
|
|
21
21
|
|
|
22
|
-
# Lifecycle validations
|
|
23
|
-
|
|
22
|
+
# Lifecycle validations.
|
|
23
|
+
#
|
|
24
|
+
# Refresh-token lifetime is deliberately NOT per-client: it is resolved
|
|
25
|
+
# globally by TokenLifetimeResolver from `oauth.refresh_token_lifetime`, so
|
|
26
|
+
# there is no `refresh_token_lifetime` column to validate here (removed in
|
|
27
|
+
# migration 20260915000000, the #765 asymmetry fix).
|
|
28
|
+
validates :access_token_lifetime, :authorization_code_lifetime,
|
|
24
29
|
presence: true, numericality: { greater_than: 0 }
|
|
25
30
|
|
|
26
31
|
# Security: public clients cannot opt out of PKCE. Public clients run in
|
data/db/migrate/20260915000000_remove_refresh_token_lifetime_from_standard_id_client_applications.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class RemoveRefreshTokenLifetimeFromStandardIdClientApplications < ActiveRecord::Migration[7.1]
|
|
2
|
+
# Drops the per-client `refresh_token_lifetime` column. It was never honoured:
|
|
3
|
+
# `TokenLifetimeResolver.refresh_token_lifetime` resolves the refresh-token
|
|
4
|
+
# lifetime GLOBALLY from `oauth.refresh_token_lifetime` and has no per-client
|
|
5
|
+
# branch, so the column advertised a knob that did nothing (the #765
|
|
6
|
+
# asymmetry). Refresh-token lifetime is a global policy by design — a client's
|
|
7
|
+
# session cadence is governed by revocation, not by a per-client lifetime
|
|
8
|
+
# (see CHANGELOG, "Revocation is the property the estate wants") — so the
|
|
9
|
+
# column is removed rather than wired up.
|
|
10
|
+
#
|
|
11
|
+
# Access- and authorization-code lifetimes remain per-client.
|
|
12
|
+
def up
|
|
13
|
+
return unless column_exists?(:standard_id_client_applications, :refresh_token_lifetime)
|
|
14
|
+
|
|
15
|
+
remove_column :standard_id_client_applications, :refresh_token_lifetime
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def down
|
|
19
|
+
return if column_exists?(:standard_id_client_applications, :refresh_token_lifetime)
|
|
20
|
+
|
|
21
|
+
add_column :standard_id_client_applications, :refresh_token_lifetime, :integer, default: 2592000
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -341,6 +341,39 @@ StandardId::ConfigSchema.define do
|
|
|
341
341
|
# and prefers an `active?`-responding record if multiple match.
|
|
342
342
|
field :audience_profile_resolver, type: :any, default: nil
|
|
343
343
|
|
|
344
|
+
# Audience → scope-vocabulary binding (the per-audience MCP scope hook).
|
|
345
|
+
#
|
|
346
|
+
# Maps each configured audience string to the Array<String> of scopes a
|
|
347
|
+
# client targeting that audience may request and be granted. This is the
|
|
348
|
+
# scope-side counterpart to `audience_profile_types`: that binds an audience
|
|
349
|
+
# to a required profile, this binds an audience to a grantable scope
|
|
350
|
+
# vocabulary. `StandardId::Oauth::AudienceScopeResolver` reads it.
|
|
351
|
+
#
|
|
352
|
+
# Values may be a single space-delimited String or an Array<String>.
|
|
353
|
+
#
|
|
354
|
+
# Example:
|
|
355
|
+
# c.oauth.audience_scopes = {
|
|
356
|
+
# "harness" => %w[mcp mcp:read mcp:eval:run mcp:prompt:write],
|
|
357
|
+
# "companion_kit" => %w[mcp mcp:read],
|
|
358
|
+
# "admin_kit" => %w[mcp mcp:read mcp:admin]
|
|
359
|
+
# }
|
|
360
|
+
#
|
|
361
|
+
# When empty (default) or the matched audience is absent from the map, the
|
|
362
|
+
# vocabulary is empty and the resolver's enforcement helpers fail OPEN
|
|
363
|
+
# (requested scopes pass through unchanged) — back-compat with apps that do
|
|
364
|
+
# not model per-audience scope vocabularies.
|
|
365
|
+
field :audience_scopes, type: :hash, default: -> { {} }
|
|
366
|
+
|
|
367
|
+
# Optional resolver for computing an audience's scope vocabulary
|
|
368
|
+
# dynamically (e.g. per-client entitlements). Called with keyword arguments
|
|
369
|
+
# `(audience:, client:, configured_scopes:)` — any subset is accepted,
|
|
370
|
+
# arguments are filtered by arity — where `configured_scopes` is the
|
|
371
|
+
# `Array<String>` from `audience_scopes` for that audience. Must return an
|
|
372
|
+
# Array<String> (or nil to fall back to the static `audience_scopes` map).
|
|
373
|
+
#
|
|
374
|
+
# When nil (default), only the static `audience_scopes` map is consulted.
|
|
375
|
+
field :audience_scope_resolver, type: :any, default: nil
|
|
376
|
+
|
|
344
377
|
# JWT signing configuration (for asymmetric algorithms)
|
|
345
378
|
# If nil, uses HS256 with Rails.application.secret_key_base
|
|
346
379
|
field :signing_key, type: :any, default: nil
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
module StandardId
|
|
2
|
+
module Oauth
|
|
3
|
+
# Resolves the scope VOCABULARY a given audience is allowed to express —
|
|
4
|
+
# the set of scope strings a client targeting that audience may request and
|
|
5
|
+
# be granted. This is the hook an app uses to declare its per-audience MCP
|
|
6
|
+
# scope vocabulary through the gem, rather than hard-coding a single flat
|
|
7
|
+
# `scopes_supported` list that every audience shares.
|
|
8
|
+
#
|
|
9
|
+
# It is the scope-side counterpart to `AudienceProfileResolver`
|
|
10
|
+
# (`c.oauth.audience_profile_types` + `c.oauth.audience_profile_resolver`):
|
|
11
|
+
# that one binds an audience to the PROFILE an account must hold; this one
|
|
12
|
+
# binds an audience to the SCOPES a client may carry. The two are read the
|
|
13
|
+
# same way and configured the same way, on purpose.
|
|
14
|
+
#
|
|
15
|
+
# Configuration (both optional, both default to "unconfigured"):
|
|
16
|
+
#
|
|
17
|
+
# # Static map: audience string => Array<String> of grantable scopes.
|
|
18
|
+
# c.oauth.audience_scopes = {
|
|
19
|
+
# "harness" => %w[mcp mcp:read mcp:eval:run mcp:prompt:write],
|
|
20
|
+
# "companion_kit" => %w[mcp mcp:read],
|
|
21
|
+
# "admin_kit" => %w[mcp mcp:read mcp:admin]
|
|
22
|
+
# }
|
|
23
|
+
#
|
|
24
|
+
# # Optional callable, for apps that compute the vocabulary dynamically
|
|
25
|
+
# # (e.g. per-client entitlements). Receives keyword args
|
|
26
|
+
# # `(audience:, client:, configured_scopes:)` — any subset is accepted,
|
|
27
|
+
# # arguments are filtered by arity — and must return an Array<String>
|
|
28
|
+
# # (or nil to fall back to the static map for that audience).
|
|
29
|
+
# c.oauth.audience_scope_resolver = ->(audience:, client:, **) {
|
|
30
|
+
# Entitlements.mcp_scopes_for(client, audience)
|
|
31
|
+
# }
|
|
32
|
+
#
|
|
33
|
+
# When an audience is UNCONFIGURED (absent from the map and the resolver
|
|
34
|
+
# returns nil / is unset), the vocabulary is empty and the enforcement
|
|
35
|
+
# helpers below fail OPEN — they pass the requested scopes through
|
|
36
|
+
# unchanged. This mirrors `audience_profile_types`: an app that does not
|
|
37
|
+
# model per-audience scope vocabularies sees no behaviour change. Narrowing
|
|
38
|
+
# only bites once an audience has an explicit vocabulary.
|
|
39
|
+
#
|
|
40
|
+
# @example
|
|
41
|
+
# StandardId::Oauth::AudienceScopeResolver.filter(
|
|
42
|
+
# requested: %w[mcp mcp:admin openid],
|
|
43
|
+
# audience: "companion_kit"
|
|
44
|
+
# ) # => ["mcp"] (mcp:admin + openid are not in companion_kit's vocabulary)
|
|
45
|
+
module AudienceScopeResolver
|
|
46
|
+
class << self
|
|
47
|
+
# The scope vocabulary for `audience` as an Array<String>.
|
|
48
|
+
#
|
|
49
|
+
# Resolution order:
|
|
50
|
+
# 1. the configured callable `audience_scope_resolver`, when set and
|
|
51
|
+
# it returns a non-nil value — filtered by arity like every other
|
|
52
|
+
# gem callable, and handed the static-map value as
|
|
53
|
+
# `configured_scopes:` so it can extend rather than replace it;
|
|
54
|
+
# 2. otherwise the static `audience_scopes` map entry;
|
|
55
|
+
# 3. otherwise `[]` (unconfigured).
|
|
56
|
+
#
|
|
57
|
+
# Always returns a de-duplicated Array<String> with blanks removed.
|
|
58
|
+
#
|
|
59
|
+
# @param audience [String, Symbol, nil]
|
|
60
|
+
# @param client [Object, nil] the ClientApplication in play, when known;
|
|
61
|
+
# passed through to the resolver callable for per-client vocabularies.
|
|
62
|
+
# @return [Array<String>]
|
|
63
|
+
def scopes_for(audience:, client: nil)
|
|
64
|
+
return [] if audience.blank?
|
|
65
|
+
|
|
66
|
+
configured = static_scopes_for(audience)
|
|
67
|
+
|
|
68
|
+
resolver = StandardId.config.oauth.audience_scope_resolver
|
|
69
|
+
if resolver.respond_to?(:call)
|
|
70
|
+
filtered = StandardId::Utils::CallableParameterFilter.filter(
|
|
71
|
+
resolver,
|
|
72
|
+
{ audience: audience.to_s, client: client, configured_scopes: configured }
|
|
73
|
+
)
|
|
74
|
+
resolved = resolver.call(**filtered)
|
|
75
|
+
return normalize(resolved) unless resolved.nil?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
configured
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# True when `audience` has a non-empty scope vocabulary. Callers use
|
|
82
|
+
# this to distinguish "audience is unconfigured, pass everything" from
|
|
83
|
+
# "audience is configured with an empty vocabulary, allow nothing".
|
|
84
|
+
def configured_for?(audience, client: nil)
|
|
85
|
+
scopes_for(audience: audience, client: client).any?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# True when `scope` is within `audience`'s vocabulary. An unconfigured
|
|
89
|
+
# audience permits every scope (fail-open) — see the module note.
|
|
90
|
+
#
|
|
91
|
+
# @param scope [String, Symbol]
|
|
92
|
+
def permits?(scope:, audience:, client: nil)
|
|
93
|
+
return false if scope.blank?
|
|
94
|
+
|
|
95
|
+
vocabulary = scopes_for(audience: audience, client: client)
|
|
96
|
+
return true if vocabulary.empty? # unconfigured -> fail open
|
|
97
|
+
|
|
98
|
+
vocabulary.include?(scope.to_s)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# The subset of `requested` that `audience`'s vocabulary permits, in the
|
|
102
|
+
# requested order, de-duplicated. An unconfigured audience returns the
|
|
103
|
+
# requested scopes unchanged (fail-open). Use this to NARROW a grant
|
|
104
|
+
# down to what the audience allows without raising.
|
|
105
|
+
#
|
|
106
|
+
# @param requested [Array<String>, String] space-delimited String or Array
|
|
107
|
+
# @return [Array<String>]
|
|
108
|
+
def filter(requested:, audience:, client: nil)
|
|
109
|
+
req = normalize(requested)
|
|
110
|
+
vocabulary = scopes_for(audience: audience, client: client)
|
|
111
|
+
return req if vocabulary.empty? # unconfigured -> pass through
|
|
112
|
+
|
|
113
|
+
allowed = vocabulary.to_set
|
|
114
|
+
req.select { |s| allowed.include?(s) }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# The scopes in `requested` that `audience`'s vocabulary does NOT permit.
|
|
118
|
+
# Empty for an unconfigured audience (nothing is out of vocabulary when
|
|
119
|
+
# there is no vocabulary).
|
|
120
|
+
#
|
|
121
|
+
# @return [Array<String>]
|
|
122
|
+
def disallowed(requested:, audience:, client: nil)
|
|
123
|
+
req = normalize(requested)
|
|
124
|
+
vocabulary = scopes_for(audience: audience, client: client)
|
|
125
|
+
return [] if vocabulary.empty?
|
|
126
|
+
|
|
127
|
+
allowed = vocabulary.to_set
|
|
128
|
+
req.reject { |s| allowed.include?(s) }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Strict, fail-closed variant for mint / registration enforcement.
|
|
132
|
+
#
|
|
133
|
+
# Returns the requested scopes (normalized) when every one is within the
|
|
134
|
+
# audience's vocabulary, and raises `StandardId::InvalidScopeError` —
|
|
135
|
+
# which renders as RFC 6749 `invalid_scope` — the moment one is not. An
|
|
136
|
+
# unconfigured audience is a no-op pass-through (fail-open), so wiring
|
|
137
|
+
# this into a mint path does not change behaviour for audiences that
|
|
138
|
+
# have not opted in.
|
|
139
|
+
#
|
|
140
|
+
# The error message names the offending scopes (which are the CLIENT's
|
|
141
|
+
# own request, not internal taxonomy) but never the full vocabulary, so
|
|
142
|
+
# the endpoint does not become a scope-enumeration oracle.
|
|
143
|
+
#
|
|
144
|
+
# @raise [StandardId::InvalidScopeError]
|
|
145
|
+
# @return [Array<String>]
|
|
146
|
+
def assert!(requested:, audience:, client: nil)
|
|
147
|
+
bad = disallowed(requested: requested, audience: audience, client: client)
|
|
148
|
+
return normalize(requested) if bad.empty?
|
|
149
|
+
|
|
150
|
+
raise StandardId::InvalidScopeError,
|
|
151
|
+
"Scope(s) not permitted for audience '#{audience}': #{bad.join(', ')}"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private
|
|
155
|
+
|
|
156
|
+
def static_scopes_for(audience)
|
|
157
|
+
mapping = StandardId.config.oauth.audience_scopes || {}
|
|
158
|
+
return [] if mapping.empty?
|
|
159
|
+
|
|
160
|
+
normalize(mapping[audience.to_s] || mapping[audience.to_sym])
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Coerce a String (space-delimited) or Array into a clean, de-duplicated
|
|
164
|
+
# Array<String>. Matches the space-delimited scope convention used by
|
|
165
|
+
# ClientApplication#scopes_array and RFC 6749.
|
|
166
|
+
def normalize(value)
|
|
167
|
+
list =
|
|
168
|
+
case value
|
|
169
|
+
when nil then []
|
|
170
|
+
when Array then value
|
|
171
|
+
else value.to_s.split(/\s+/)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
list.map { |s| s.to_s.strip }.reject(&:blank?).uniq
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
data/lib/standard_id/version.rb
CHANGED
data/lib/standard_id.rb
CHANGED
|
@@ -29,6 +29,7 @@ require "standard_id/api/token_manager"
|
|
|
29
29
|
require "standard_id/api/authentication_guard"
|
|
30
30
|
require "standard_id/utils/callable_parameter_filter"
|
|
31
31
|
require "standard_id/oauth/audience_profile_resolver"
|
|
32
|
+
require "standard_id/oauth/audience_scope_resolver"
|
|
32
33
|
require "standard_id/oauth/base_request_flow"
|
|
33
34
|
require "standard_id/oauth/token_lifetime_resolver"
|
|
34
35
|
require "standard_id/oauth/oauth_session_persistence"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: standard_id
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.41.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jaryl Sim
|
|
@@ -230,6 +230,7 @@ files:
|
|
|
230
230
|
- db/migrate/20260414200000_add_target_created_at_index_to_code_challenges.rb
|
|
231
231
|
- db/migrate/20260416180511_add_partial_indexes_for_active_session_and_challenge_lookups.rb
|
|
232
232
|
- db/migrate/20260611000000_create_standard_id_client_grants.rb
|
|
233
|
+
- db/migrate/20260915000000_remove_refresh_token_lifetime_from_standard_id_client_applications.rb
|
|
233
234
|
- lib/generators/standard_id/install/install_generator.rb
|
|
234
235
|
- lib/generators/standard_id/install/templates/standard_id.rb
|
|
235
236
|
- lib/standard_id.rb
|
|
@@ -261,6 +262,7 @@ files:
|
|
|
261
262
|
- lib/standard_id/http_client.rb
|
|
262
263
|
- lib/standard_id/jwt_service.rb
|
|
263
264
|
- lib/standard_id/oauth/audience_profile_resolver.rb
|
|
265
|
+
- lib/standard_id/oauth/audience_scope_resolver.rb
|
|
264
266
|
- lib/standard_id/oauth/authorization_code_authorization_flow.rb
|
|
265
267
|
- lib/standard_id/oauth/authorization_code_flow.rb
|
|
266
268
|
- lib/standard_id/oauth/authorization_flow.rb
|