token_authority 0.2.1 → 0.3.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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -1
  3. data/README.md +52 -14
  4. data/app/controllers/concerns/token_authority/initial_access_token_authentication.rb +2 -2
  5. data/app/controllers/token_authority/protected_resource_metadata_controller.rb +39 -0
  6. data/app/helpers/token_authority/authorization_grants_helper.rb +2 -3
  7. data/app/models/concerns/token_authority/claim_validatable.rb +2 -2
  8. data/app/models/concerns/token_authority/resourceable.rb +2 -2
  9. data/app/models/token_authority/access_token.rb +2 -2
  10. data/app/models/token_authority/access_token_request.rb +1 -1
  11. data/app/models/token_authority/authorization_request.rb +2 -2
  12. data/app/models/token_authority/authorization_server_metadata.rb +4 -4
  13. data/app/models/token_authority/client.rb +4 -4
  14. data/app/models/token_authority/client_metadata_document.rb +2 -2
  15. data/app/models/token_authority/client_registration_request.rb +5 -5
  16. data/app/models/token_authority/jwks_fetcher.rb +1 -1
  17. data/app/models/token_authority/protected_resource_metadata.rb +110 -31
  18. data/app/models/token_authority/refresh_token.rb +2 -2
  19. data/app/models/token_authority/refresh_token_request.rb +1 -1
  20. data/lib/generators/token_authority/install/templates/token_authority.rb +100 -114
  21. data/lib/token_authority/configuration.rb +345 -175
  22. data/lib/token_authority/errors.rb +29 -0
  23. data/lib/token_authority/routing/constraints.rb +2 -2
  24. data/lib/token_authority/routing/routes.rb +74 -16
  25. data/lib/token_authority/version.rb +1 -1
  26. data/lib/token_authority.rb +2 -2
  27. metadata +2 -2
  28. data/app/controllers/token_authority/resource_metadata_controller.rb +0 -12
@@ -218,4 +218,33 @@ module TokenAuthority
218
218
  super
219
219
  end
220
220
  end
221
+
222
+ # Raised when no protected resource configuration exists for the requested subdomain.
223
+ #
224
+ # This error occurs in the RFC 9728 protected resource metadata endpoint when:
225
+ # 1. A subdomain-specific request arrives but that subdomain isn't in protected_resources
226
+ # 2. The fallback protected_resource configuration is also empty or nil
227
+ # 3. A bare domain request arrives but protected_resource isn't configured
228
+ #
229
+ # The ResourceMetadataController catches this error and returns HTTP 404, which is
230
+ # semantically correct: the client is asking about a resource that doesn't exist in
231
+ # the configuration. This differs from a 500 error which would imply a server problem.
232
+ #
233
+ # This separation of concerns (model raises domain error, controller maps to HTTP status)
234
+ # keeps the model focused on business logic without coupling it to HTTP semantics.
235
+ #
236
+ # @example Subdomain not configured
237
+ # # config.protected_resources = { "api" => {...} }
238
+ # # Request to mcp.example.com/.well-known/oauth-protected-resource
239
+ # # Raises this error because "mcp" isn't configured
240
+ #
241
+ # @see ResourceMetadataController#show
242
+ # @since 0.3.0
243
+ class ResourceNotConfiguredError < StandardError
244
+ # Creates a new ResourceNotConfiguredError.
245
+ # @param msg [String] custom error message
246
+ def initialize(msg = "No protected resource configuration found for this subdomain")
247
+ super
248
+ end
249
+ end
221
250
  end
@@ -62,9 +62,9 @@ module TokenAuthority
62
62
  # Determines if dynamic client registration is enabled in the configuration.
63
63
  #
64
64
  # @param _request [ActionDispatch::Request] the Rails request object (unused)
65
- # @return [Boolean] true if RFC 7591 dynamic registration is enabled
65
+ # @return [Boolean] true if dynamic client registration is enabled
66
66
  def matches?(_request)
67
- TokenAuthority.config.rfc_7591_enabled
67
+ TokenAuthority.config.dcr_enabled
68
68
  end
69
69
  end
70
70
  end
@@ -4,36 +4,94 @@ module ActionDispatch
4
4
  module Routing
5
5
  class Mapper
6
6
  ##
7
- # Adds all TokenAuthority routes including OAuth 2.0 metadata endpoints
8
- # (RFC 8414, RFC 9728) and mounts the TokenAuthority engine.
7
+ # Registers authorization server routes including OAuth endpoints and RFC 8414 metadata.
9
8
  #
10
- # Both RFC 8414 and RFC 9728 require the metadata endpoints to be at the
11
- # root level `/.well-known/` path, not under the engine mount path.
9
+ # This is the primary route helper for setting up OAuth authorization server functionality.
10
+ # It registers two things:
11
+ # 1. The RFC 8414 metadata endpoint at /.well-known/oauth-authorization-server
12
+ # 2. The full TokenAuthority engine at your chosen mount path
12
13
  #
13
- # @param at [String] the path where TokenAuthority engine is mounted (default: "/oauth")
14
+ # The metadata endpoint receives the mount path as a parameter so it can generate
15
+ # correct URLs for the authorization and token endpoints in its response. This allows
16
+ # clients to discover the OAuth endpoints dynamically.
14
17
  #
15
- # @example
18
+ # @param at [String] mount path for OAuth endpoints (default: "/oauth")
19
+ #
20
+ # @example Basic usage
16
21
  # Rails.application.routes.draw do
17
- # token_authority_routes # Mounts at default "/oauth"
22
+ # token_authority_auth_server_routes
23
+ # # Creates routes at /oauth/authorize, /oauth/token, etc.
24
+ # # Metadata at /.well-known/oauth-authorization-server
18
25
  # end
19
26
  #
20
- # @example Custom mount path
27
+ # @example Authorization server on dedicated subdomain
21
28
  # Rails.application.routes.draw do
22
- # token_authority_routes(at: "/auth")
29
+ # constraints subdomain: "auth" do
30
+ # token_authority_auth_server_routes
31
+ # # Creates https://auth.example.com/oauth/authorize, etc.
32
+ # end
23
33
  # end
24
- def token_authority_routes(at: "/oauth")
25
- # RFC 8414: Authorization Server Metadata
34
+ #
35
+ # @example Custom mount path
36
+ # token_authority_auth_server_routes(at: "/oauth2")
37
+ # # Creates /oauth2/authorize, /oauth2/token instead of /oauth/*
38
+ #
39
+ def token_authority_auth_server_routes(at: "/oauth")
26
40
  get "/.well-known/oauth-authorization-server",
27
41
  to: "token_authority/metadata#show",
28
42
  defaults: {mount_path: at}
29
43
 
30
- # RFC 9728: Protected Resource Metadata
31
- get "/.well-known/oauth-protected-resource",
32
- to: "token_authority/resource_metadata#show",
33
- defaults: {mount_path: at}
34
-
35
44
  mount TokenAuthority::Engine => at
36
45
  end
46
+
47
+ ##
48
+ # Registers RFC 9728 Protected Resource Metadata endpoint for client discovery.
49
+ #
50
+ # Creates a route at /.well-known/oauth-protected-resource that returns metadata
51
+ # about the protected resource, including which authorization servers can issue
52
+ # tokens for it, what scopes are supported, and how to present bearer tokens.
53
+ #
54
+ # This helper is designed to be called multiple times with different subdomain
55
+ # constraints, enabling a single Rails application to serve metadata for multiple
56
+ # protected resources. The controller extracts the subdomain from each request and
57
+ # looks it up as a symbol key in config.resources to find the appropriate metadata.
58
+ #
59
+ # For single-resource deployments, configure one entry in config.resources - it will
60
+ # be used as the fallback for any request. For multi-resource deployments, configure
61
+ # entries for each subdomain. The first entry in config.resources is used as the
62
+ # fallback when no subdomain matches.
63
+ #
64
+ # Returns 404 if config.resources is empty.
65
+ #
66
+ # @example Single protected resource
67
+ # Rails.application.routes.draw do
68
+ # token_authority_auth_server_routes
69
+ # token_authority_protected_resource_route
70
+ # # Serves metadata from first entry in config.resources
71
+ # end
72
+ #
73
+ # @example Multiple protected resources at different subdomains
74
+ # Rails.application.routes.draw do
75
+ # token_authority_auth_server_routes
76
+ #
77
+ # # REST API protected resource
78
+ # constraints subdomain: "api" do
79
+ # token_authority_protected_resource_route
80
+ # # Serves metadata from config.resources[:api]
81
+ # end
82
+ #
83
+ # # MCP server protected resource
84
+ # constraints subdomain: "mcp" do
85
+ # token_authority_protected_resource_route
86
+ # # Serves metadata from config.resources[:mcp]
87
+ # end
88
+ # end
89
+ #
90
+ # @see https://www.rfc-editor.org/rfc/rfc9728.html RFC 9728
91
+ def token_authority_protected_resource_route
92
+ get "/.well-known/oauth-protected-resource",
93
+ to: "token_authority/protected_resource_metadata#show"
94
+ end
37
95
  end
38
96
  end
39
97
  end
@@ -2,5 +2,5 @@ module TokenAuthority
2
2
  # The current version of the TokenAuthority gem.
3
3
  #
4
4
  # @return [String] the version string in semantic versioning format
5
- VERSION = "0.2.1"
5
+ VERSION = "0.3.0"
6
6
  end
@@ -19,8 +19,8 @@ require "token_authority/routing/routes"
19
19
  # @example Basic configuration
20
20
  # TokenAuthority.configure do |config|
21
21
  # config.secret_key = Rails.application.credentials.secret_key_base
22
- # config.rfc_9068_audience_url = "https://api.example.com"
23
- # config.rfc_9068_issuer_url = "https://example.com"
22
+ # config.token_audience_url = "https://api.example.com"
23
+ # config.token_issuer_url = "https://example.com"
24
24
  # end
25
25
  #
26
26
  # @since 0.2.0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: token_authority
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dick Davis
@@ -56,7 +56,7 @@ files:
56
56
  - app/controllers/token_authority/authorizations_controller.rb
57
57
  - app/controllers/token_authority/clients_controller.rb
58
58
  - app/controllers/token_authority/metadata_controller.rb
59
- - app/controllers/token_authority/resource_metadata_controller.rb
59
+ - app/controllers/token_authority/protected_resource_metadata_controller.rb
60
60
  - app/controllers/token_authority/sessions_controller.rb
61
61
  - app/helpers/token_authority/authorization_grants_helper.rb
62
62
  - app/models/concerns/token_authority/claim_validatable.rb
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TokenAuthority
4
- ##
5
- # Controller for RFC 9728 OAuth 2.0 Protected Resource Metadata
6
- class ResourceMetadataController < ActionController::API
7
- def show
8
- metadata = ProtectedResourceMetadata.new(mount_path: params[:mount_path])
9
- render json: metadata.to_h
10
- end
11
- end
12
- end