11# frozen_string_literal: true
22
33require "securerandom"
4+ require "net/http"
45require_relative "../../../serialization_utils"
5- require_relative "../client_registry"
6- require_relative "../state_registry"
76require_relative "../provider"
87
98module MCP
@@ -39,28 +38,33 @@ class McpAuthorizationServerProvider
3938 include OAuthAuthorizationServerProvider
4039 include SerializationUtils
4140
41+ FIVE_MINUTES_IN_SECONDS = 300
42+
4243 def initialize (
4344 auth_server_settings :,
44- client_registry : nil ,
45- state_registry : nil
45+ client_registry :,
46+ state_registry :,
47+ auth_code_registry :,
48+ token_registry :
4649 )
4750 @settings = auth_server_settings
48- @client_registry = client_registry || InMemoryClientRegistry . new
49- @state_registry = state_registry || InMemoryStateRegistry . new
51+ @client_registry = client_registry
52+ @state_registry = state_registry
53+ @auth_code_registry = auth_code_registry
54+ @token_registry = token_registry
5055 end
5156
5257 def get_client ( client_id )
53- @client_registry . find_by_id ( client_id )
58+ @client_registry . find_client ( client_id )
5459 end
5560
5661 def register_client ( client_info )
57- @client_registry . create ( client_info )
62+ @client_registry . create_client ( client_info )
5863 end
5964
6065 def authorize ( client_info :, auth_params :)
6166 state = auth_params . state || SecureRandom . hex ( 16 )
62-
63- @state_registry . create ( state , to_h ( auth_params ) )
67+ @state_registry . create_state ( state , to_h ( auth_params ) )
6468
6569 auth_url = URI ( @settings . auth_server_authorization_endpoint )
6670 auth_url . query = URI . encode_www_form ( [
@@ -73,6 +77,67 @@ def authorize(client_info:, auth_params:)
7377
7478 auth_url . to_s
7579 end
80+
81+ def authorize_callback ( code :, state :)
82+ state_data = @state_registry . find_state ( state )
83+ raise Errors ::AuthorizationError . invalid_request ( "invalid state parameter" ) if state_data . nil?
84+
85+ access_token = query_access_token! ( {
86+ client_id : @settings . client_id ,
87+ client_secret : @settings . client_secret ,
88+ code :,
89+ redirect_uri : @settings . mcp_callback_endpoint ,
90+ grant_type : "authorization_code" ,
91+ } )
92+
93+ mcp_auth_code = "mcp_#{ SecureRandom . hex ( 16 ) } "
94+ auth_code = MCP ::Auth ::Server ::AuthorizationCode . new (
95+ code : mcp_auth_code ,
96+ client_id : state_data [ :client_id ] ,
97+ redirect_uri : state_data [ :redirect_uri ] ,
98+ redirect_uri_provided_explicitly : state_data [ :redirect_uri_provided_explicitly ] ,
99+ expires_at : Time . now . to_i + FIVE_MINUTES_IN_SECONDS ,
100+ scopes : [ "mcp:user" ] ,
101+ code_challenge : state_data [ :code_challenge ] ,
102+ )
103+ @auth_code_registry . create_auth_code ( mcp_auth_code , auth_code )
104+ @token_registry . create_token ( mcp_auth_code , AccessToken . new (
105+ token : access_token ,
106+ client_id : state_data [ :client_id ] ,
107+ scopes : @settings . auth_server_scopes ,
108+ expires_at : nil ,
109+ ) )
110+
111+ redirect_uri = URI ( state_data [ :redirect_uri ] )
112+ redirect_uri . query = URI . encode_www_form ( [
113+ [ "code" , mcp_auth_code ] ,
114+ [ "state" , state ] ,
115+ ] )
116+ @state_registry . delete_state ( state )
117+
118+ redirect_uri
119+ end
120+
121+ private
122+
123+ def query_access_token! ( data )
124+ uri = URI ( @settings . auth_server_token_endpoint )
125+ response = Net ::HTTP . post_form ( uri , stringify_keys ( data ) )
126+ raise Errors ::AuthorizationError . new (
127+ error_code : Errors ::AuthorizationError ::INVALID_REQUEST ,
128+ message : "failed to exchange code for token" ,
129+ ) unless response . is_a? ( Net ::HTTPOK )
130+
131+ data = JSON . parse ( response . body )
132+ if data . key? ( "error" )
133+ raise Errors ::AuthorizationError . new (
134+ error_code : data [ "error" ] ,
135+ message : data [ "error_description" ] || data [ "error" ] ,
136+ )
137+ end
138+
139+ data [ "access_token" ]
140+ end
76141 end
77142 end
78143 end
0 commit comments