standard_id 0.31.0 → 0.33.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 +344 -0
- data/README.md +14 -0
- data/app/controllers/standard_id/api/oauth/introspections_controller.rb +164 -0
- data/app/controllers/standard_id/api/oauth/revocations_controller.rb +12 -61
- data/app/controllers/standard_id/api/well_known/oauth_authorization_server_controller.rb +7 -4
- data/app/controllers/standard_id/api/well_known/openid_configuration_controller.rb +7 -4
- data/app/models/concerns/standard_id/account_associations.rb +18 -5
- data/app/models/standard_id/identifier.rb +4 -1
- data/app/models/standard_id/session.rb +171 -14
- data/config/routes/api.rb +5 -0
- data/lib/generators/standard_id/install/templates/standard_id.rb +122 -2
- data/lib/standard_id/association_strict_loading.rb +121 -0
- data/lib/standard_id/config/schema.rb +115 -12
- data/lib/standard_id/engine.rb +33 -0
- data/lib/standard_id/oauth/discovery_document.rb +73 -12
- data/lib/standard_id/oauth/discovery_resolver.rb +158 -0
- data/lib/standard_id/provider_registry.rb +66 -13
- data/lib/standard_id/routing.rb +133 -0
- data/lib/standard_id/version.rb +1 -1
- data/lib/standard_id.rb +3 -0
- metadata +5 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
require "action_dispatch/routing/mapper"
|
|
2
|
+
|
|
3
|
+
module StandardId
|
|
4
|
+
# Route-mapper extensions, available inside `Rails.application.routes.draw`.
|
|
5
|
+
module Routing
|
|
6
|
+
# Documents this helper can draw, mapped to their path and controller.
|
|
7
|
+
WELL_KNOWN_DOCUMENTS = {
|
|
8
|
+
oauth_authorization_server: {
|
|
9
|
+
path: "oauth-authorization-server",
|
|
10
|
+
to: "standard_id/api/well_known/oauth_authorization_server#show"
|
|
11
|
+
},
|
|
12
|
+
openid_configuration: {
|
|
13
|
+
path: "openid-configuration",
|
|
14
|
+
to: "standard_id/api/well_known/openid_configuration#show"
|
|
15
|
+
},
|
|
16
|
+
jwks: {
|
|
17
|
+
path: "jwks.json",
|
|
18
|
+
to: "standard_id/api/well_known/jwks#show"
|
|
19
|
+
}
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
# Draw StandardId's discovery documents at the ORIGIN ROOT.
|
|
23
|
+
#
|
|
24
|
+
# # config/routes.rb
|
|
25
|
+
# Rails.application.routes.draw do
|
|
26
|
+
# standard_id_well_known_routes at: "/api/v1"
|
|
27
|
+
# namespace :api do
|
|
28
|
+
# mount StandardId::ApiEngine, at: "/", as: :standard_id_api
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# ## Why the gem cannot just route these itself
|
|
33
|
+
#
|
|
34
|
+
# The engine's own `.well-known` routes live INSIDE its mount, so an app
|
|
35
|
+
# mounting ApiEngine at `/api/v1` serves them at
|
|
36
|
+
# `/api/v1/.well-known/openid-configuration`. But RFC 8615 clients probe the
|
|
37
|
+
# **origin root**, which is outside any engine mount — a mapper helper in the
|
|
38
|
+
# host's routes file is the only place these can be drawn.
|
|
39
|
+
#
|
|
40
|
+
# ## The path-inserted form, and why it is not optional
|
|
41
|
+
#
|
|
42
|
+
# For each document this draws BOTH:
|
|
43
|
+
#
|
|
44
|
+
# /.well-known/oauth-authorization-server
|
|
45
|
+
# /.well-known/oauth-authorization-server/api/v1 <- RFC 8414 §3.1
|
|
46
|
+
#
|
|
47
|
+
# The second is the spec's form for an issuer that carries a path: the
|
|
48
|
+
# well-known segment is INSERTED BEFORE the issuer's path rather than appended
|
|
49
|
+
# to it. It looks redundant and it is not. It is the URL Claude Code actually
|
|
50
|
+
# requests in production against a path-carrying issuer; without it the client
|
|
51
|
+
# 404s, falls back to issuer-relative defaults (hitting the engine's real
|
|
52
|
+
# `/authorize` instead of a host audience shim), and every subsequent
|
|
53
|
+
# authenticated request 401s. That failure is remote from its cause and hard
|
|
54
|
+
# to diagnose, which is why the route is drawn for you rather than documented
|
|
55
|
+
# as an extra step.
|
|
56
|
+
#
|
|
57
|
+
# Both forms serve the same document. The mount path is stamped into the
|
|
58
|
+
# route defaults so DiscoveryResolver can build endpoint URLs off it — a root
|
|
59
|
+
# route has no SCRIPT_NAME to read it from.
|
|
60
|
+
#
|
|
61
|
+
# @param at [String] the path ApiEngine is mounted at ("/" or "" for root).
|
|
62
|
+
# Used both to build the path-inserted routes and to resolve endpoint URLs.
|
|
63
|
+
# @param only [Array<Symbol>, Symbol, nil] restrict to these documents.
|
|
64
|
+
# Keys: :oauth_authorization_server, :openid_configuration, :jwks. Apps
|
|
65
|
+
# that keep a host-owned metadata controller but want the gem's JWKS at the
|
|
66
|
+
# root pass `only: :jwks`.
|
|
67
|
+
# @param except [Array<Symbol>, Symbol, nil] the inverse of `only`.
|
|
68
|
+
# @param extra_paths [Array<String>] additional path-inserted suffixes beyond
|
|
69
|
+
# `at:`. For serving the same document under a second issuer path (e.g. a
|
|
70
|
+
# protected resource at `/mcp`) without a second route block.
|
|
71
|
+
# @param path_inserted [Boolean] set false to draw only the bare root forms.
|
|
72
|
+
# Defaults to true; turning it off is what reintroduces the Claude Code
|
|
73
|
+
# failure above, so it exists only for apps that route those themselves.
|
|
74
|
+
def standard_id_well_known_routes(at:, only: nil, except: nil, extra_paths: [], path_inserted: true)
|
|
75
|
+
mount_path = StandardId::Oauth::DiscoveryResolver.normalize_path(at)
|
|
76
|
+
documents = StandardId::Routing.selected_documents(only: only, except: except)
|
|
77
|
+
|
|
78
|
+
suffixes = path_inserted ? ([mount_path] + Array(extra_paths)).map { |p| StandardId::Oauth::DiscoveryResolver.normalize_path(p) } : []
|
|
79
|
+
suffixes = suffixes.reject(&:empty?).uniq
|
|
80
|
+
|
|
81
|
+
defaults = { StandardId::Oauth::DiscoveryResolver::MOUNT_PATH_PARAM => mount_path }
|
|
82
|
+
|
|
83
|
+
scope ".well-known" do
|
|
84
|
+
documents.each do |name, config|
|
|
85
|
+
get config[:path], to: config[:to], as: :"standard_id_root_#{name}", defaults: defaults
|
|
86
|
+
|
|
87
|
+
# jwks.json is a concrete file path, not a metadata document whose
|
|
88
|
+
# location RFC 8414 §3.1 relocates — a path-inserted variant of it
|
|
89
|
+
# would advertise a URL no spec describes.
|
|
90
|
+
next if name == :jwks
|
|
91
|
+
|
|
92
|
+
suffixes.each_with_index do |suffix, index|
|
|
93
|
+
get "#{config[:path]}#{suffix}",
|
|
94
|
+
to: config[:to],
|
|
95
|
+
as: :"standard_id_root_#{name}_at_#{index}",
|
|
96
|
+
defaults: defaults
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @return [Hash] the WELL_KNOWN_DOCUMENTS entries `only:`/`except:` select
|
|
103
|
+
def self.selected_documents(only: nil, except: nil)
|
|
104
|
+
if only.present? && except.present?
|
|
105
|
+
raise ArgumentError, "standard_id_well_known_routes accepts `only:` or `except:`, not both"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
keys = WELL_KNOWN_DOCUMENTS.keys
|
|
109
|
+
requested = Array(only).map(&:to_sym) + Array(except).map(&:to_sym)
|
|
110
|
+
unknown = requested - keys
|
|
111
|
+
if unknown.any?
|
|
112
|
+
raise ArgumentError,
|
|
113
|
+
"Unknown StandardId well-known document(s): #{unknown.join(', ')}. Valid: #{keys.join(', ')}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
selected = if only.present?
|
|
117
|
+
Array(only).map(&:to_sym)
|
|
118
|
+
elsif except.present?
|
|
119
|
+
keys - Array(except).map(&:to_sym)
|
|
120
|
+
else
|
|
121
|
+
keys
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
WELL_KNOWN_DOCUMENTS.slice(*selected)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Included directly rather than through a load hook: ActionDispatch has no
|
|
130
|
+
# `:action_dispatch_routing_mapper` hook, and this file is required from
|
|
131
|
+
# lib/standard_id.rb during Bundler.require — long before any routes file is
|
|
132
|
+
# evaluated — so the mapper method is defined by the time a host calls it.
|
|
133
|
+
ActionDispatch::Routing::Mapper.include(StandardId::Routing)
|
data/lib/standard_id/version.rb
CHANGED
data/lib/standard_id.rb
CHANGED
|
@@ -6,6 +6,7 @@ require "standard_id/api_engine"
|
|
|
6
6
|
require "standard_id/config_schema"
|
|
7
7
|
require "standard_id/config/schema"
|
|
8
8
|
require "standard_id/scope_config"
|
|
9
|
+
require "standard_id/association_strict_loading"
|
|
9
10
|
require "standard_id/errors"
|
|
10
11
|
require "standard_id/events"
|
|
11
12
|
require "standard_id/events/subscribers/base"
|
|
@@ -44,7 +45,9 @@ require "standard_id/oauth/subflows/base"
|
|
|
44
45
|
require "standard_id/oauth/subflows/traditional_code_grant"
|
|
45
46
|
require "standard_id/oauth/subflows/social_login_grant"
|
|
46
47
|
require "standard_id/oauth/passwordless_otp_flow"
|
|
48
|
+
require "standard_id/oauth/discovery_resolver"
|
|
47
49
|
require "standard_id/oauth/discovery_document"
|
|
50
|
+
require "standard_id/routing"
|
|
48
51
|
require "standard_id/oauth/consent_payload"
|
|
49
52
|
require "standard_id/oauth/client_registration"
|
|
50
53
|
require "standard_id/passwordless/base_strategy"
|
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.33.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jaryl Sim
|
|
@@ -133,6 +133,7 @@ files:
|
|
|
133
133
|
- app/controllers/standard_id/api/base_controller.rb
|
|
134
134
|
- app/controllers/standard_id/api/oauth/base_controller.rb
|
|
135
135
|
- app/controllers/standard_id/api/oauth/callback/providers_controller.rb
|
|
136
|
+
- app/controllers/standard_id/api/oauth/introspections_controller.rb
|
|
136
137
|
- app/controllers/standard_id/api/oauth/registrations_controller.rb
|
|
137
138
|
- app/controllers/standard_id/api/oauth/revocations_controller.rb
|
|
138
139
|
- app/controllers/standard_id/api/oauth/tokens_controller.rb
|
|
@@ -238,6 +239,7 @@ files:
|
|
|
238
239
|
- lib/standard_id/api/session_manager.rb
|
|
239
240
|
- lib/standard_id/api/token_manager.rb
|
|
240
241
|
- lib/standard_id/api_engine.rb
|
|
242
|
+
- lib/standard_id/association_strict_loading.rb
|
|
241
243
|
- lib/standard_id/authorization_bypass.rb
|
|
242
244
|
- lib/standard_id/bearer_token_extraction.rb
|
|
243
245
|
- lib/standard_id/config/callable_validator.rb
|
|
@@ -267,6 +269,7 @@ files:
|
|
|
267
269
|
- lib/standard_id/oauth/client_registration.rb
|
|
268
270
|
- lib/standard_id/oauth/consent_payload.rb
|
|
269
271
|
- lib/standard_id/oauth/discovery_document.rb
|
|
272
|
+
- lib/standard_id/oauth/discovery_resolver.rb
|
|
270
273
|
- lib/standard_id/oauth/implicit_authorization_flow.rb
|
|
271
274
|
- lib/standard_id/oauth/oauth_session_persistence.rb
|
|
272
275
|
- lib/standard_id/oauth/password_flow.rb
|
|
@@ -287,6 +290,7 @@ files:
|
|
|
287
290
|
- lib/standard_id/provider_registry.rb
|
|
288
291
|
- lib/standard_id/providers/base.rb
|
|
289
292
|
- lib/standard_id/rate_limit_store.rb
|
|
293
|
+
- lib/standard_id/routing.rb
|
|
290
294
|
- lib/standard_id/scope_config.rb
|
|
291
295
|
- lib/standard_id/session_type_resolver.rb
|
|
292
296
|
- lib/standard_id/testing.rb
|