token_authority 0.1.0 → 0.2.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 +23 -0
- data/README.md +199 -7
- data/app/controllers/concerns/token_authority/client_authentication.rb +141 -0
- data/app/controllers/concerns/token_authority/controller_event_logging.rb +98 -0
- data/app/controllers/concerns/token_authority/initial_access_token_authentication.rb +35 -0
- data/app/controllers/concerns/token_authority/token_authentication.rb +128 -0
- data/app/controllers/token_authority/authorization_grants_controller.rb +119 -0
- data/app/controllers/token_authority/authorizations_controller.rb +105 -0
- data/app/controllers/token_authority/clients_controller.rb +99 -0
- data/app/controllers/token_authority/metadata_controller.rb +12 -0
- data/app/controllers/token_authority/resource_metadata_controller.rb +12 -0
- data/app/controllers/token_authority/sessions_controller.rb +228 -0
- data/app/helpers/token_authority/authorization_grants_helper.rb +27 -0
- data/app/models/concerns/token_authority/claim_validatable.rb +95 -0
- data/app/models/concerns/token_authority/event_logging.rb +144 -0
- data/app/models/concerns/token_authority/resourceable.rb +111 -0
- data/app/models/concerns/token_authority/scopeable.rb +105 -0
- data/app/models/concerns/token_authority/session_creatable.rb +101 -0
- data/app/models/token_authority/access_token.rb +127 -0
- data/app/models/token_authority/access_token_request.rb +193 -0
- data/app/models/token_authority/authorization_grant.rb +119 -0
- data/app/models/token_authority/authorization_request.rb +276 -0
- data/app/models/token_authority/authorization_server_metadata.rb +101 -0
- data/app/models/token_authority/client.rb +263 -0
- data/app/models/token_authority/client_id_resolver.rb +114 -0
- data/app/models/token_authority/client_metadata_document.rb +164 -0
- data/app/models/token_authority/client_metadata_document_cache.rb +33 -0
- data/app/models/token_authority/client_metadata_document_fetcher.rb +266 -0
- data/app/models/token_authority/client_registration_request.rb +214 -0
- data/app/models/token_authority/client_registration_response.rb +58 -0
- data/app/models/token_authority/jwks_cache.rb +37 -0
- data/app/models/token_authority/jwks_fetcher.rb +70 -0
- data/app/models/token_authority/protected_resource_metadata.rb +74 -0
- data/app/models/token_authority/refresh_token.rb +110 -0
- data/app/models/token_authority/refresh_token_request.rb +116 -0
- data/app/models/token_authority/session.rb +193 -0
- data/app/models/token_authority/software_statement.rb +70 -0
- data/app/views/token_authority/authorization_grants/new.html.erb +25 -0
- data/app/views/token_authority/client_error.html.erb +8 -0
- data/config/locales/token_authority.en.yml +248 -0
- data/config/routes.rb +29 -0
- data/lib/generators/token_authority/install/install_generator.rb +61 -0
- data/lib/generators/token_authority/install/templates/create_token_authority_tables.rb.erb +116 -0
- data/lib/generators/token_authority/install/templates/token_authority.rb +247 -0
- data/lib/token_authority/configuration.rb +397 -0
- data/lib/token_authority/engine.rb +34 -0
- data/lib/token_authority/errors.rb +221 -0
- data/lib/token_authority/instrumentation.rb +80 -0
- data/lib/token_authority/instrumentation_log_subscriber.rb +62 -0
- data/lib/token_authority/json_web_token.rb +78 -0
- data/lib/token_authority/log_event_subscriber.rb +43 -0
- data/lib/token_authority/routing/constraints.rb +71 -0
- data/lib/token_authority/routing/routes.rb +39 -0
- data/lib/token_authority/version.rb +4 -1
- data/lib/token_authority.rb +30 -1
- metadata +65 -5
- data/app/assets/stylesheets/token_authority/application.css +0 -15
- data/app/controllers/token_authority/application_controller.rb +0 -4
- data/app/helpers/token_authority/application_helper.rb +0 -4
- data/app/views/layouts/token_authority/application.html.erb +0 -17
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
TokenAuthority.configure do |config|
|
|
4
|
+
# ==========================================================================
|
|
5
|
+
# General
|
|
6
|
+
# ==========================================================================
|
|
7
|
+
|
|
8
|
+
# The secret key used for signing JWT tokens and generating client secrets.
|
|
9
|
+
# This should be a secure, random string. By default, it uses the application's
|
|
10
|
+
# secret_key_base from credentials or configuration.
|
|
11
|
+
config.secret_key = Rails.application.credentials.secret_key_base || Rails.application.secret_key_base
|
|
12
|
+
|
|
13
|
+
# ==========================================================================
|
|
14
|
+
# JWT Access Tokens (RFC 9068)
|
|
15
|
+
# ==========================================================================
|
|
16
|
+
|
|
17
|
+
# The audience URL for JWT tokens. This is typically your API's base URL.
|
|
18
|
+
# Used as the "aud" (audience) claim in issued tokens.
|
|
19
|
+
config.rfc_9068_audience_url = ENV.fetch("TOKEN_AUTHORITY_AUDIENCE_URL", "http://localhost:3000/api/")
|
|
20
|
+
|
|
21
|
+
# The issuer URL for JWT tokens. This is typically your application's base URL.
|
|
22
|
+
# Used as the "iss" (issuer) claim in issued tokens.
|
|
23
|
+
config.rfc_9068_issuer_url = ENV.fetch("TOKEN_AUTHORITY_ISSUER_URL", "http://localhost:3000/")
|
|
24
|
+
|
|
25
|
+
# Default duration for access tokens in seconds (5 minutes).
|
|
26
|
+
# This value is used when creating new clients without explicit durations.
|
|
27
|
+
# config.rfc_9068_default_access_token_duration = 300
|
|
28
|
+
|
|
29
|
+
# Default duration for refresh tokens in seconds (14 days).
|
|
30
|
+
# This value is used when creating new clients without explicit durations.
|
|
31
|
+
# config.rfc_9068_default_refresh_token_duration = 1_209_600
|
|
32
|
+
|
|
33
|
+
# ==========================================================================
|
|
34
|
+
# User Authentication
|
|
35
|
+
# ==========================================================================
|
|
36
|
+
|
|
37
|
+
# The authenticatable controller for the authorization grants controller (consent screen).
|
|
38
|
+
# This controller must implement:
|
|
39
|
+
# - authenticate_user! (before_action that ensures user is logged in)
|
|
40
|
+
# - current_user (returns the currently authenticated user)
|
|
41
|
+
#
|
|
42
|
+
# For Devise users, ApplicationController already has these methods.
|
|
43
|
+
# For other authentication systems, either:
|
|
44
|
+
# 1. Implement these methods on ApplicationController, or
|
|
45
|
+
# 2. Set this to a controller that provides these methods
|
|
46
|
+
# Default: "ApplicationController"
|
|
47
|
+
# config.authenticatable_controller = "ApplicationController"
|
|
48
|
+
|
|
49
|
+
# The class name of your user model. This is used for the belongs_to association
|
|
50
|
+
# in TokenAuthority::AuthorizationGrant.
|
|
51
|
+
# Default: "User"
|
|
52
|
+
# config.user_class = "User"
|
|
53
|
+
|
|
54
|
+
# ==========================================================================
|
|
55
|
+
# UI/Layout
|
|
56
|
+
# ==========================================================================
|
|
57
|
+
|
|
58
|
+
# The layout used for the OAuth consent screen.
|
|
59
|
+
config.consent_page_layout = "application"
|
|
60
|
+
|
|
61
|
+
# The layout used for error pages (e.g., invalid redirect URL).
|
|
62
|
+
config.error_page_layout = "application"
|
|
63
|
+
|
|
64
|
+
# ==========================================================================
|
|
65
|
+
# Server Metadata (RFC 8414)
|
|
66
|
+
# ==========================================================================
|
|
67
|
+
|
|
68
|
+
# URL to developer documentation for your OAuth server.
|
|
69
|
+
# Included in the /.well-known/oauth-authorization-server response.
|
|
70
|
+
# config.rfc_8414_service_documentation = "https://example.com/docs/oauth"
|
|
71
|
+
|
|
72
|
+
# Note: scopes_supported in the metadata response is automatically derived
|
|
73
|
+
# from the keys of config.scopes (see Scopes section below).
|
|
74
|
+
|
|
75
|
+
# ==========================================================================
|
|
76
|
+
# Protected Resource Metadata (RFC 9728)
|
|
77
|
+
# ==========================================================================
|
|
78
|
+
|
|
79
|
+
# The protected resource's identifier URL.
|
|
80
|
+
# Defaults to rfc_9068_issuer_url if not set.
|
|
81
|
+
# config.rfc_9728_resource = "https://api.example.com/"
|
|
82
|
+
|
|
83
|
+
# Scopes accepted by the protected resource.
|
|
84
|
+
# Falls back to config.scopes keys if not set.
|
|
85
|
+
# config.rfc_9728_scopes_supported = ["api:read", "api:write"]
|
|
86
|
+
|
|
87
|
+
# List of authorization server issuer URLs that can issue tokens for this resource.
|
|
88
|
+
# Defaults to the local authorization server (rfc_9068_issuer_url) if not set.
|
|
89
|
+
# config.rfc_9728_authorization_servers = ["https://auth.example.com"]
|
|
90
|
+
|
|
91
|
+
# Token presentation methods supported by the resource (e.g., "header", "body", "query").
|
|
92
|
+
# config.rfc_9728_bearer_methods_supported = ["header"]
|
|
93
|
+
|
|
94
|
+
# URL to the resource's JSON Web Key Set (JWKS).
|
|
95
|
+
# config.rfc_9728_jwks_uri = "https://api.example.com/.well-known/jwks.json"
|
|
96
|
+
|
|
97
|
+
# Human-readable name for the protected resource.
|
|
98
|
+
# config.rfc_9728_resource_name = "Example API"
|
|
99
|
+
|
|
100
|
+
# URL to developer documentation for the protected resource.
|
|
101
|
+
# config.rfc_9728_resource_documentation = "https://example.com/docs/api"
|
|
102
|
+
|
|
103
|
+
# URL to the resource's privacy policy.
|
|
104
|
+
# config.rfc_9728_resource_policy_uri = "https://example.com/privacy"
|
|
105
|
+
|
|
106
|
+
# URL to the resource's terms of service.
|
|
107
|
+
# config.rfc_9728_resource_tos_uri = "https://example.com/tos"
|
|
108
|
+
|
|
109
|
+
# ==========================================================================
|
|
110
|
+
# Scopes
|
|
111
|
+
# ==========================================================================
|
|
112
|
+
|
|
113
|
+
# Configure allowed scopes with human-friendly display names.
|
|
114
|
+
# Keys are the scope strings (used as the allowlist), values are display names
|
|
115
|
+
# shown on the consent screen.
|
|
116
|
+
#
|
|
117
|
+
# Set to nil or {} to disable scope validation entirely.
|
|
118
|
+
# When configured, only these scopes are allowed in authorization requests.
|
|
119
|
+
# config.scopes = {
|
|
120
|
+
# "read" => "Read access to your data",
|
|
121
|
+
# "write" => "Write access to your data"
|
|
122
|
+
# }
|
|
123
|
+
|
|
124
|
+
# Require the scope parameter in authorization requests.
|
|
125
|
+
# When true, clients must specify at least one scope.
|
|
126
|
+
# config.require_scope = false
|
|
127
|
+
|
|
128
|
+
# ==========================================================================
|
|
129
|
+
# Resource Indicators (RFC 8707)
|
|
130
|
+
# ==========================================================================
|
|
131
|
+
|
|
132
|
+
# Configure allowed resources with human-friendly display names.
|
|
133
|
+
# Keys are the resource URIs (used as the allowlist), values are display names
|
|
134
|
+
# shown on the consent screen.
|
|
135
|
+
#
|
|
136
|
+
# Set to nil or {} to disable resource indicators entirely.
|
|
137
|
+
# When configured, only these resources are allowed in authorization requests.
|
|
138
|
+
# config.rfc_8707_resources = {
|
|
139
|
+
# "https://api.example.com/" => "Main API",
|
|
140
|
+
# "https://billing.example.com/" => "Billing Service"
|
|
141
|
+
# }
|
|
142
|
+
|
|
143
|
+
# Require the resource parameter in authorization requests.
|
|
144
|
+
# When true, clients must specify at least one resource.
|
|
145
|
+
# config.rfc_8707_require_resource = false
|
|
146
|
+
|
|
147
|
+
# ==========================================================================
|
|
148
|
+
# Dynamic Client Registration (RFC 7591)
|
|
149
|
+
# ==========================================================================
|
|
150
|
+
|
|
151
|
+
# Enable dynamic client registration endpoint (/register).
|
|
152
|
+
# When enabled, clients can register programmatically via POST requests.
|
|
153
|
+
# Disabled by default for security - enable only if you need this feature.
|
|
154
|
+
# config.rfc_7591_enabled = false
|
|
155
|
+
|
|
156
|
+
# Require an initial access token for client registration.
|
|
157
|
+
# When enabled, registration requests must include a Bearer token.
|
|
158
|
+
# config.rfc_7591_require_initial_access_token = false
|
|
159
|
+
|
|
160
|
+
# Validator proc for initial access tokens.
|
|
161
|
+
# Must return true if the token is valid, false otherwise.
|
|
162
|
+
# Example:
|
|
163
|
+
# config.rfc_7591_initial_access_token_validator = ->(token) {
|
|
164
|
+
# token == ENV["REGISTRATION_ACCESS_TOKEN"]
|
|
165
|
+
# }
|
|
166
|
+
|
|
167
|
+
# Allowed grant types for dynamically registered clients.
|
|
168
|
+
# config.rfc_7591_allowed_grant_types = %w[authorization_code refresh_token]
|
|
169
|
+
|
|
170
|
+
# Allowed response types for dynamically registered clients.
|
|
171
|
+
# config.rfc_7591_allowed_response_types = %w[code]
|
|
172
|
+
|
|
173
|
+
# Allowed token endpoint authentication methods.
|
|
174
|
+
# Options: none, client_secret_basic, client_secret_post, client_secret_jwt, private_key_jwt
|
|
175
|
+
# config.rfc_7591_allowed_token_endpoint_auth_methods = %w[none client_secret_basic client_secret_post client_secret_jwt private_key_jwt]
|
|
176
|
+
|
|
177
|
+
# Client secret expiration duration in seconds (nil = never expires).
|
|
178
|
+
# config.rfc_7591_client_secret_expiration = nil
|
|
179
|
+
|
|
180
|
+
# JWKS for verifying software statements (signed JWTs with client metadata).
|
|
181
|
+
# Set to a JWKS hash to enable software statement verification.
|
|
182
|
+
# config.rfc_7591_software_statement_jwks = nil
|
|
183
|
+
|
|
184
|
+
# Require software statements for client registration.
|
|
185
|
+
# When enabled, registration requests must include a valid software_statement.
|
|
186
|
+
# config.rfc_7591_software_statement_required = false
|
|
187
|
+
|
|
188
|
+
# Cache TTL in seconds for fetched JWKS from jwks_uri (default: 1 hour).
|
|
189
|
+
# config.rfc_7591_jwks_cache_ttl = 3600
|
|
190
|
+
|
|
191
|
+
# ==========================================================================
|
|
192
|
+
# Client Metadata Document (draft-ietf-oauth-client-id-metadata-document)
|
|
193
|
+
# ==========================================================================
|
|
194
|
+
|
|
195
|
+
# URL-based client identifiers allow clients to use HTTPS URLs as their client_id.
|
|
196
|
+
# The authorization server fetches client metadata from the URL at runtime.
|
|
197
|
+
# This enables lightweight, decentralized client registration.
|
|
198
|
+
#
|
|
199
|
+
# SECURITY CONSIDERATION: By default, any HTTPS URL can be used as a client_id,
|
|
200
|
+
# meaning any server on the internet can act as an OAuth client to your
|
|
201
|
+
# authorization server. This is appropriate for MCP servers and open ecosystems.
|
|
202
|
+
# For restricted access, configure allowed_hosts to limit which domains can
|
|
203
|
+
# host client metadata documents.
|
|
204
|
+
#
|
|
205
|
+
# Example for production with restricted access:
|
|
206
|
+
# config.client_metadata_document_allowed_hosts = ["trusted-partner.com", "*.mycompany.com"]
|
|
207
|
+
|
|
208
|
+
# Enable URL-based client identifiers (default: true).
|
|
209
|
+
# config.client_metadata_document_enabled = true
|
|
210
|
+
|
|
211
|
+
# Allowed hosts for client metadata document URLs (default: nil = all hosts).
|
|
212
|
+
# config.client_metadata_document_allowed_hosts = nil
|
|
213
|
+
|
|
214
|
+
# Blocked hosts for client metadata document URLs (default: []).
|
|
215
|
+
# Example: ["internal.example.com", "*.local"]
|
|
216
|
+
# config.client_metadata_document_blocked_hosts = []
|
|
217
|
+
|
|
218
|
+
# Cache TTL in seconds for fetched metadata documents (default: 1 hour).
|
|
219
|
+
# config.client_metadata_document_cache_ttl = 3600
|
|
220
|
+
|
|
221
|
+
# Maximum response size in bytes (default: 5KB).
|
|
222
|
+
# config.client_metadata_document_max_response_size = 5120
|
|
223
|
+
|
|
224
|
+
# Connection and read timeouts in seconds (default: 5 each).
|
|
225
|
+
# config.client_metadata_document_connect_timeout = 5
|
|
226
|
+
# config.client_metadata_document_read_timeout = 5
|
|
227
|
+
|
|
228
|
+
# ==========================================================================
|
|
229
|
+
# Event Logging
|
|
230
|
+
# ==========================================================================
|
|
231
|
+
|
|
232
|
+
# TokenAuthority emits structured events using Rails 8.1's event reporting.
|
|
233
|
+
# Events are automatically logged to Rails.logger when enabled.
|
|
234
|
+
#
|
|
235
|
+
# Events include: authorization requests, consent actions, token exchanges,
|
|
236
|
+
# token refreshes, revocations, client authentication, and security events
|
|
237
|
+
# (e.g., token theft detection).
|
|
238
|
+
|
|
239
|
+
# Enable event logging (default: true).
|
|
240
|
+
# When enabled, events are emitted and logged to Rails.logger.
|
|
241
|
+
# config.event_logging_enabled = true
|
|
242
|
+
|
|
243
|
+
# Enable debug events (default: false).
|
|
244
|
+
# Debug events provide detailed information useful during development,
|
|
245
|
+
# such as PKCE validation steps and cache hits/misses.
|
|
246
|
+
# config.event_logging_debug_events = false
|
|
247
|
+
end
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TokenAuthority
|
|
4
|
+
# Configuration class for TokenAuthority engine settings.
|
|
5
|
+
#
|
|
6
|
+
# This class manages all configuration options for the OAuth 2.1 provider,
|
|
7
|
+
# including JWT settings, user authentication integration, UI customization,
|
|
8
|
+
# and RFC-specific feature flags.
|
|
9
|
+
#
|
|
10
|
+
# Configuration is typically set in a Rails initializer using a configure block
|
|
11
|
+
# that yields this configuration object.
|
|
12
|
+
#
|
|
13
|
+
# @example Basic configuration
|
|
14
|
+
# TokenAuthority.configure do |config|
|
|
15
|
+
# config.secret_key = Rails.application.credentials.secret_key_base
|
|
16
|
+
# config.user_class = "User"
|
|
17
|
+
# config.rfc_9068_audience_url = "https://api.example.com"
|
|
18
|
+
# config.rfc_9068_issuer_url = "https://example.com"
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# @example Enabling scopes
|
|
22
|
+
# TokenAuthority.configure do |config|
|
|
23
|
+
# config.scopes = {
|
|
24
|
+
# "read" => "Read access to your data",
|
|
25
|
+
# "write" => "Write access to your data"
|
|
26
|
+
# }
|
|
27
|
+
# config.require_scope = true
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# @since 0.2.0
|
|
31
|
+
class Configuration
|
|
32
|
+
# @!attribute [rw] secret_key
|
|
33
|
+
# The secret key used for JWT signing and HMAC operations.
|
|
34
|
+
# This should be a secure random string, typically derived from Rails credentials.
|
|
35
|
+
# @return [String] the secret key
|
|
36
|
+
attr_accessor :secret_key
|
|
37
|
+
|
|
38
|
+
# @!attribute [rw] rfc_9068_audience_url
|
|
39
|
+
# The default audience (aud) claim for JWT access tokens per RFC 9068.
|
|
40
|
+
# Identifies the intended recipient of the token (typically the API server).
|
|
41
|
+
# @return [String, nil] the audience URL
|
|
42
|
+
attr_accessor :rfc_9068_audience_url
|
|
43
|
+
|
|
44
|
+
# @!attribute [rw] rfc_9068_issuer_url
|
|
45
|
+
# The issuer (iss) claim for JWT access tokens per RFC 9068.
|
|
46
|
+
# Identifies the authorization server that issued the token.
|
|
47
|
+
# @return [String, nil] the issuer URL
|
|
48
|
+
attr_accessor :rfc_9068_issuer_url
|
|
49
|
+
|
|
50
|
+
# @!attribute [rw] rfc_9068_default_access_token_duration
|
|
51
|
+
# Default lifetime for access tokens in seconds.
|
|
52
|
+
# @return [Integer] duration in seconds (default: 300)
|
|
53
|
+
attr_accessor :rfc_9068_default_access_token_duration
|
|
54
|
+
|
|
55
|
+
# @!attribute [rw] rfc_9068_default_refresh_token_duration
|
|
56
|
+
# Default lifetime for refresh tokens in seconds.
|
|
57
|
+
# @return [Integer] duration in seconds (default: 1,209,600)
|
|
58
|
+
attr_accessor :rfc_9068_default_refresh_token_duration
|
|
59
|
+
|
|
60
|
+
# @!attribute [rw] authenticatable_controller
|
|
61
|
+
# The controller class name that provides authentication methods.
|
|
62
|
+
# Must implement `authenticate_user!` and `current_user` methods.
|
|
63
|
+
# @return [String] controller class name (default: "ApplicationController")
|
|
64
|
+
attr_accessor :authenticatable_controller
|
|
65
|
+
|
|
66
|
+
# @!attribute [rw] user_class
|
|
67
|
+
# The user model class name in the host application.
|
|
68
|
+
# @return [String] user class name (default: "User")
|
|
69
|
+
attr_accessor :user_class
|
|
70
|
+
|
|
71
|
+
# @!attribute [rw] consent_page_layout
|
|
72
|
+
# The layout to use for the OAuth consent screen.
|
|
73
|
+
# @return [String] layout name (default: "application")
|
|
74
|
+
attr_accessor :consent_page_layout
|
|
75
|
+
|
|
76
|
+
# @!attribute [rw] error_page_layout
|
|
77
|
+
# The layout to use for OAuth error pages.
|
|
78
|
+
# @return [String] layout name (default: "application")
|
|
79
|
+
attr_accessor :error_page_layout
|
|
80
|
+
|
|
81
|
+
# @!attribute [rw] rfc_8414_service_documentation
|
|
82
|
+
# URL for service documentation in authorization server metadata per RFC 8414.
|
|
83
|
+
# @return [String, nil] documentation URL
|
|
84
|
+
attr_accessor :rfc_8414_service_documentation
|
|
85
|
+
|
|
86
|
+
# @!attribute [rw] scopes
|
|
87
|
+
# Hash mapping scope strings to human-readable descriptions.
|
|
88
|
+
# Set to nil or empty hash to disable scopes.
|
|
89
|
+
# @example
|
|
90
|
+
# config.scopes = {
|
|
91
|
+
# "read" => "Read access to your data",
|
|
92
|
+
# "write" => "Write access to your data"
|
|
93
|
+
# }
|
|
94
|
+
# @return [Hash{String => String}, nil] scope mappings
|
|
95
|
+
attr_accessor :scopes
|
|
96
|
+
|
|
97
|
+
# @!attribute [rw] require_scope
|
|
98
|
+
# Whether clients must include a scope parameter in authorization requests.
|
|
99
|
+
# @return [Boolean] true if scope is required (default: false)
|
|
100
|
+
attr_accessor :require_scope
|
|
101
|
+
|
|
102
|
+
# @!attribute [rw] rfc_9728_resource
|
|
103
|
+
# The resource URI for protected resource metadata per RFC 9728.
|
|
104
|
+
# @return [String, nil] resource URI
|
|
105
|
+
attr_accessor :rfc_9728_resource
|
|
106
|
+
|
|
107
|
+
# @!attribute [rw] rfc_9728_scopes_supported
|
|
108
|
+
# Array of OAuth scopes supported by the protected resource per RFC 9728.
|
|
109
|
+
# @return [Array<String>, nil] supported scopes
|
|
110
|
+
attr_accessor :rfc_9728_scopes_supported
|
|
111
|
+
|
|
112
|
+
# @!attribute [rw] rfc_9728_authorization_servers
|
|
113
|
+
# Array of authorization server issuer URLs per RFC 9728.
|
|
114
|
+
# @return [Array<String>, nil] authorization server URLs
|
|
115
|
+
attr_accessor :rfc_9728_authorization_servers
|
|
116
|
+
|
|
117
|
+
# @!attribute [rw] rfc_9728_bearer_methods_supported
|
|
118
|
+
# Array of bearer token methods supported per RFC 9728.
|
|
119
|
+
# @return [Array<String>, nil] bearer methods
|
|
120
|
+
attr_accessor :rfc_9728_bearer_methods_supported
|
|
121
|
+
|
|
122
|
+
# @!attribute [rw] rfc_9728_jwks_uri
|
|
123
|
+
# URL to the JWKS for protected resource per RFC 9728.
|
|
124
|
+
# @return [String, nil] JWKS URI
|
|
125
|
+
attr_accessor :rfc_9728_jwks_uri
|
|
126
|
+
|
|
127
|
+
# @!attribute [rw] rfc_9728_resource_name
|
|
128
|
+
# Human-readable name of the protected resource per RFC 9728.
|
|
129
|
+
# @return [String, nil] resource name
|
|
130
|
+
attr_accessor :rfc_9728_resource_name
|
|
131
|
+
|
|
132
|
+
# @!attribute [rw] rfc_9728_resource_documentation
|
|
133
|
+
# URL to documentation for the protected resource per RFC 9728.
|
|
134
|
+
# @return [String, nil] documentation URL
|
|
135
|
+
attr_accessor :rfc_9728_resource_documentation
|
|
136
|
+
|
|
137
|
+
# @!attribute [rw] rfc_9728_resource_policy_uri
|
|
138
|
+
# URL to privacy policy for the protected resource per RFC 9728.
|
|
139
|
+
# @return [String, nil] policy URI
|
|
140
|
+
attr_accessor :rfc_9728_resource_policy_uri
|
|
141
|
+
|
|
142
|
+
# @!attribute [rw] rfc_9728_resource_tos_uri
|
|
143
|
+
# URL to terms of service for the protected resource per RFC 9728.
|
|
144
|
+
# @return [String, nil] TOS URI
|
|
145
|
+
attr_accessor :rfc_9728_resource_tos_uri
|
|
146
|
+
|
|
147
|
+
# @!attribute [rw] rfc_7591_enabled
|
|
148
|
+
# Enable dynamic client registration per RFC 7591.
|
|
149
|
+
# @return [Boolean] true if enabled (default: false)
|
|
150
|
+
attr_accessor :rfc_7591_enabled
|
|
151
|
+
|
|
152
|
+
# @!attribute [rw] rfc_7591_require_initial_access_token
|
|
153
|
+
# Require initial access token for client registration per RFC 7591.
|
|
154
|
+
# @return [Boolean] true if required (default: false)
|
|
155
|
+
attr_accessor :rfc_7591_require_initial_access_token
|
|
156
|
+
|
|
157
|
+
# @!attribute [rw] rfc_7591_initial_access_token_validator
|
|
158
|
+
# Callable object to validate initial access tokens.
|
|
159
|
+
# Should accept a token string and return true/false.
|
|
160
|
+
# @return [Proc, nil] validator callable
|
|
161
|
+
attr_accessor :rfc_7591_initial_access_token_validator
|
|
162
|
+
|
|
163
|
+
# @!attribute [rw] rfc_7591_allowed_grant_types
|
|
164
|
+
# Array of grant types allowed during client registration per RFC 7591.
|
|
165
|
+
# @return [Array<String>] allowed grant types
|
|
166
|
+
attr_accessor :rfc_7591_allowed_grant_types
|
|
167
|
+
|
|
168
|
+
# @!attribute [rw] rfc_7591_allowed_response_types
|
|
169
|
+
# Array of response types allowed during client registration per RFC 7591.
|
|
170
|
+
# @return [Array<String>] allowed response types
|
|
171
|
+
attr_accessor :rfc_7591_allowed_response_types
|
|
172
|
+
|
|
173
|
+
# @!attribute [rw] rfc_7591_allowed_scopes
|
|
174
|
+
# Array of scopes allowed during client registration per RFC 7591.
|
|
175
|
+
# @return [Array<String>, nil] allowed scopes
|
|
176
|
+
attr_accessor :rfc_7591_allowed_scopes
|
|
177
|
+
|
|
178
|
+
# @!attribute [rw] rfc_7591_allowed_token_endpoint_auth_methods
|
|
179
|
+
# Array of token endpoint authentication methods allowed per RFC 7591.
|
|
180
|
+
# @return [Array<String>] allowed auth methods
|
|
181
|
+
attr_accessor :rfc_7591_allowed_token_endpoint_auth_methods
|
|
182
|
+
|
|
183
|
+
# @!attribute [rw] rfc_7591_client_secret_expiration
|
|
184
|
+
# Duration in seconds before client secrets expire, or nil for no expiration.
|
|
185
|
+
# @return [Integer, nil] expiration duration in seconds
|
|
186
|
+
attr_accessor :rfc_7591_client_secret_expiration
|
|
187
|
+
|
|
188
|
+
# @!attribute [rw] rfc_7591_software_statement_jwks
|
|
189
|
+
# JWKS for verifying software statements during registration per RFC 7591.
|
|
190
|
+
# @return [Hash, nil] JWKS
|
|
191
|
+
attr_accessor :rfc_7591_software_statement_jwks
|
|
192
|
+
|
|
193
|
+
# @!attribute [rw] rfc_7591_software_statement_required
|
|
194
|
+
# Require software statements during client registration per RFC 7591.
|
|
195
|
+
# @return [Boolean] true if required (default: false)
|
|
196
|
+
attr_accessor :rfc_7591_software_statement_required
|
|
197
|
+
|
|
198
|
+
# @!attribute [rw] rfc_7591_jwks_cache_ttl
|
|
199
|
+
# Time-to-live for cached JWKS in seconds.
|
|
200
|
+
# @return [Integer] TTL in seconds (default: 3600)
|
|
201
|
+
attr_accessor :rfc_7591_jwks_cache_ttl
|
|
202
|
+
|
|
203
|
+
# @!attribute [rw] client_metadata_document_enabled
|
|
204
|
+
# Enable support for client metadata documents (URL-based client IDs).
|
|
205
|
+
# @return [Boolean] true if enabled (default: true)
|
|
206
|
+
attr_accessor :client_metadata_document_enabled
|
|
207
|
+
|
|
208
|
+
# @!attribute [rw] client_metadata_document_cache_ttl
|
|
209
|
+
# Time-to-live for cached client metadata documents in seconds.
|
|
210
|
+
# @return [Integer] TTL in seconds (default: 3600)
|
|
211
|
+
attr_accessor :client_metadata_document_cache_ttl
|
|
212
|
+
|
|
213
|
+
# @!attribute [rw] client_metadata_document_max_response_size
|
|
214
|
+
# Maximum size in bytes for client metadata document HTTP responses.
|
|
215
|
+
# @return [Integer] max size in bytes (default: 5120)
|
|
216
|
+
attr_accessor :client_metadata_document_max_response_size
|
|
217
|
+
|
|
218
|
+
# @!attribute [rw] client_metadata_document_allowed_hosts
|
|
219
|
+
# Whitelist of hosts allowed for client metadata document URLs.
|
|
220
|
+
# @return [Array<String>, nil] allowed hosts
|
|
221
|
+
attr_accessor :client_metadata_document_allowed_hosts
|
|
222
|
+
|
|
223
|
+
# @!attribute [rw] client_metadata_document_blocked_hosts
|
|
224
|
+
# Blacklist of hosts blocked for client metadata document URLs.
|
|
225
|
+
# @return [Array<String>] blocked hosts (default: [])
|
|
226
|
+
attr_accessor :client_metadata_document_blocked_hosts
|
|
227
|
+
|
|
228
|
+
# @!attribute [rw] client_metadata_document_connect_timeout
|
|
229
|
+
# Connection timeout in seconds for fetching client metadata documents.
|
|
230
|
+
# @return [Integer] timeout in seconds (default: 5)
|
|
231
|
+
attr_accessor :client_metadata_document_connect_timeout
|
|
232
|
+
|
|
233
|
+
# @!attribute [rw] client_metadata_document_read_timeout
|
|
234
|
+
# Read timeout in seconds for fetching client metadata documents.
|
|
235
|
+
# @return [Integer] timeout in seconds (default: 5)
|
|
236
|
+
attr_accessor :client_metadata_document_read_timeout
|
|
237
|
+
|
|
238
|
+
# @!attribute [rw] rfc_8707_resources
|
|
239
|
+
# Hash mapping resource URIs to human-readable descriptions per RFC 8707.
|
|
240
|
+
# Set to nil or empty hash to disable resource indicators.
|
|
241
|
+
# @example
|
|
242
|
+
# config.rfc_8707_resources = {
|
|
243
|
+
# "https://api.example.com" => "Main API",
|
|
244
|
+
# "https://files.example.com" => "File Storage API"
|
|
245
|
+
# }
|
|
246
|
+
# @return [Hash{String => String}, nil] resource mappings
|
|
247
|
+
attr_accessor :rfc_8707_resources
|
|
248
|
+
|
|
249
|
+
# @!attribute [rw] rfc_8707_require_resource
|
|
250
|
+
# Whether clients must include a resource parameter per RFC 8707.
|
|
251
|
+
# @return [Boolean] true if required (default: false)
|
|
252
|
+
attr_accessor :rfc_8707_require_resource
|
|
253
|
+
|
|
254
|
+
# @!attribute [rw] event_logging_enabled
|
|
255
|
+
# Enable structured event logging for OAuth flows and security events.
|
|
256
|
+
# @return [Boolean] true if enabled (default: true)
|
|
257
|
+
attr_accessor :event_logging_enabled
|
|
258
|
+
|
|
259
|
+
# @!attribute [rw] event_logging_debug_events
|
|
260
|
+
# Enable debug-level event logging for troubleshooting.
|
|
261
|
+
# @return [Boolean] true if enabled (default: false)
|
|
262
|
+
attr_accessor :event_logging_debug_events
|
|
263
|
+
|
|
264
|
+
# @!attribute [rw] instrumentation_enabled
|
|
265
|
+
# Enable ActiveSupport::Notifications instrumentation for performance monitoring.
|
|
266
|
+
# @return [Boolean] true if enabled (default: true)
|
|
267
|
+
attr_accessor :instrumentation_enabled
|
|
268
|
+
|
|
269
|
+
def initialize
|
|
270
|
+
# General
|
|
271
|
+
@secret_key = nil
|
|
272
|
+
|
|
273
|
+
# User Authentication
|
|
274
|
+
@authenticatable_controller = "ApplicationController"
|
|
275
|
+
@user_class = "User"
|
|
276
|
+
|
|
277
|
+
# UI/Layout
|
|
278
|
+
@consent_page_layout = "application"
|
|
279
|
+
@error_page_layout = "application"
|
|
280
|
+
|
|
281
|
+
# Scopes
|
|
282
|
+
@scopes = nil
|
|
283
|
+
@require_scope = false
|
|
284
|
+
|
|
285
|
+
# JWT Access Tokens (RFC 9068)
|
|
286
|
+
@rfc_9068_audience_url = nil
|
|
287
|
+
@rfc_9068_issuer_url = nil
|
|
288
|
+
@rfc_9068_default_access_token_duration = 300 # 5 minutes in seconds
|
|
289
|
+
@rfc_9068_default_refresh_token_duration = 1_209_600 # 14 days in seconds
|
|
290
|
+
|
|
291
|
+
# Server Metadata (RFC 8414)
|
|
292
|
+
@rfc_8414_service_documentation = nil
|
|
293
|
+
|
|
294
|
+
# Protected Resource Metadata (RFC 9728)
|
|
295
|
+
@rfc_9728_resource = nil
|
|
296
|
+
@rfc_9728_scopes_supported = nil
|
|
297
|
+
@rfc_9728_authorization_servers = nil
|
|
298
|
+
@rfc_9728_bearer_methods_supported = nil
|
|
299
|
+
@rfc_9728_jwks_uri = nil
|
|
300
|
+
@rfc_9728_resource_name = nil
|
|
301
|
+
@rfc_9728_resource_documentation = nil
|
|
302
|
+
@rfc_9728_resource_policy_uri = nil
|
|
303
|
+
@rfc_9728_resource_tos_uri = nil
|
|
304
|
+
|
|
305
|
+
# Dynamic Client Registration (RFC 7591)
|
|
306
|
+
@rfc_7591_enabled = false
|
|
307
|
+
@rfc_7591_require_initial_access_token = false
|
|
308
|
+
@rfc_7591_initial_access_token_validator = nil
|
|
309
|
+
@rfc_7591_allowed_grant_types = %w[authorization_code refresh_token]
|
|
310
|
+
@rfc_7591_allowed_response_types = %w[code]
|
|
311
|
+
@rfc_7591_allowed_scopes = nil
|
|
312
|
+
@rfc_7591_allowed_token_endpoint_auth_methods = %w[none client_secret_basic client_secret_post client_secret_jwt private_key_jwt]
|
|
313
|
+
@rfc_7591_client_secret_expiration = nil
|
|
314
|
+
@rfc_7591_software_statement_jwks = nil
|
|
315
|
+
@rfc_7591_software_statement_required = false
|
|
316
|
+
@rfc_7591_jwks_cache_ttl = 3600
|
|
317
|
+
|
|
318
|
+
# Client Metadata Document (draft-ietf-oauth-client-id-metadata-document)
|
|
319
|
+
@client_metadata_document_enabled = true
|
|
320
|
+
@client_metadata_document_cache_ttl = 3600
|
|
321
|
+
@client_metadata_document_max_response_size = 5120
|
|
322
|
+
@client_metadata_document_allowed_hosts = nil
|
|
323
|
+
@client_metadata_document_blocked_hosts = []
|
|
324
|
+
@client_metadata_document_connect_timeout = 5
|
|
325
|
+
@client_metadata_document_read_timeout = 5
|
|
326
|
+
|
|
327
|
+
# Resource Indicators (RFC 8707)
|
|
328
|
+
@rfc_8707_resources = nil
|
|
329
|
+
@rfc_8707_require_resource = false
|
|
330
|
+
|
|
331
|
+
# Event Logging
|
|
332
|
+
@event_logging_enabled = true
|
|
333
|
+
@event_logging_debug_events = false
|
|
334
|
+
|
|
335
|
+
# Instrumentation
|
|
336
|
+
@instrumentation_enabled = true
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# Checks whether the scopes feature is enabled.
|
|
340
|
+
# Scopes are considered enabled when the scopes attribute is a non-empty hash.
|
|
341
|
+
#
|
|
342
|
+
# @return [Boolean] true if scopes are enabled
|
|
343
|
+
def scopes_enabled?
|
|
344
|
+
scopes.is_a?(Hash) && scopes.any?
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# Checks whether RFC 8707 resource indicators are enabled.
|
|
348
|
+
# Resource indicators are considered enabled when rfc_8707_resources is a non-empty hash.
|
|
349
|
+
#
|
|
350
|
+
# @return [Boolean] true if resource indicators are enabled
|
|
351
|
+
def rfc_8707_enabled?
|
|
352
|
+
rfc_8707_resources.is_a?(Hash) && rfc_8707_resources.any?
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Validates the configuration for internal consistency.
|
|
356
|
+
# Ensures that required features are properly configured before use.
|
|
357
|
+
#
|
|
358
|
+
# @raise [ConfigurationError] if require_scope is true but scopes are not configured
|
|
359
|
+
# @raise [ConfigurationError] if rfc_8707_require_resource is true but resources are not configured
|
|
360
|
+
# @return [void]
|
|
361
|
+
def validate!
|
|
362
|
+
if require_scope && !scopes_enabled?
|
|
363
|
+
raise ConfigurationError, "require_scope is true but no scopes are configured"
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
if rfc_8707_require_resource && !rfc_8707_enabled?
|
|
367
|
+
raise ConfigurationError, "rfc_8707_require_resource is true but no rfc_8707_resources are configured"
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
class << self
|
|
373
|
+
# Returns the current configuration instance.
|
|
374
|
+
# Creates a new configuration with default values if one doesn't exist.
|
|
375
|
+
#
|
|
376
|
+
# @return [Configuration] the configuration instance
|
|
377
|
+
def config
|
|
378
|
+
@config ||= Configuration.new
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Yields the configuration instance for setup in initializers.
|
|
382
|
+
# This is the primary way to configure TokenAuthority in a Rails application.
|
|
383
|
+
#
|
|
384
|
+
# @example
|
|
385
|
+
# TokenAuthority.configure do |config|
|
|
386
|
+
# config.secret_key = Rails.application.credentials.secret_key_base
|
|
387
|
+
# config.user_class = "User"
|
|
388
|
+
# end
|
|
389
|
+
#
|
|
390
|
+
# @yield [config] configuration block
|
|
391
|
+
# @yieldparam config [Configuration] the configuration instance to modify
|
|
392
|
+
# @return [void]
|
|
393
|
+
def configure
|
|
394
|
+
yield(config)
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
end
|
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
module TokenAuthority
|
|
2
|
+
# Rails engine that integrates TokenAuthority into host applications.
|
|
3
|
+
#
|
|
4
|
+
# The engine uses an isolated namespace to avoid conflicts with the host application's
|
|
5
|
+
# models and controllers. It sets up initializers for event logging and instrumentation
|
|
6
|
+
# based on the configuration settings.
|
|
7
|
+
#
|
|
8
|
+
# @note This engine is automatically loaded when the TokenAuthority gem is required
|
|
9
|
+
# in a Rails application.
|
|
10
|
+
#
|
|
11
|
+
# @since 0.2.0
|
|
2
12
|
class Engine < ::Rails::Engine
|
|
3
13
|
isolate_namespace TokenAuthority
|
|
14
|
+
|
|
15
|
+
# Registers the event subscriber to capture and log OAuth flow events.
|
|
16
|
+
# Only runs if event_logging_enabled is true in the configuration.
|
|
17
|
+
#
|
|
18
|
+
# Events are published through Rails.event and include authorization grants,
|
|
19
|
+
# token exchanges, and security-related activities.
|
|
20
|
+
initializer "token_authority.event_logging", after: :load_config_initializers do
|
|
21
|
+
if TokenAuthority.config.event_logging_enabled
|
|
22
|
+
require "token_authority/log_event_subscriber"
|
|
23
|
+
Rails.event.subscribe(TokenAuthority::LogEventSubscriber.new)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Attaches the instrumentation log subscriber to capture performance metrics.
|
|
28
|
+
# Only runs if instrumentation_enabled is true in the configuration.
|
|
29
|
+
#
|
|
30
|
+
# Instrumentation uses ActiveSupport::Notifications to track timing and
|
|
31
|
+
# performance of key operations like JWT encoding/decoding and session creation.
|
|
32
|
+
initializer "token_authority.instrumentation", after: :load_config_initializers do
|
|
33
|
+
if TokenAuthority.config.instrumentation_enabled
|
|
34
|
+
require "token_authority/instrumentation_log_subscriber"
|
|
35
|
+
TokenAuthority::InstrumentationLogSubscriber.subscribe!
|
|
36
|
+
end
|
|
37
|
+
end
|
|
4
38
|
end
|
|
5
39
|
end
|