Class: MCPClient::Auth::OAuthProvider
- Inherits:
-
Object
- Object
- MCPClient::Auth::OAuthProvider
- Defined in:
- lib/mcp_client/auth/oauth_provider.rb
Overview
OAuth 2.1 provider for MCP client authentication Handles the complete OAuth flow including server discovery, client registration, authorization, token exchange, and refresh
Defined Under Namespace
Classes: MemoryStorage
Instance Attribute Summary collapse
-
#logger ⇒ Logger
Logger instance.
-
#redirect_uri ⇒ String
OAuth redirect URI.
-
#scope ⇒ String?
OAuth scope.
-
#server_url ⇒ String
The MCP server URL (normalized).
-
#storage ⇒ Object
Storage backend for tokens and client info.
Instance Method Summary collapse
-
#access_token ⇒ Token?
Get current access token (refresh if needed).
-
#apply_authorization(request) ⇒ void
Apply OAuth authorization to HTTP request.
-
#complete_authorization_flow(code, state) ⇒ Token
Complete OAuth authorization flow with authorization code.
-
#handle_unauthorized_response(response) ⇒ ResourceMetadata?
Handle 401 Unauthorized response (for server discovery).
-
#initialize(server_url:, redirect_uri: 'http://localhost:8080/callback', scope: nil, logger: nil, storage: nil) ⇒ OAuthProvider
constructor
Initialize OAuth provider.
-
#start_authorization_flow ⇒ String
Start OAuth authorization flow.
Constructor Details
#initialize(server_url:, redirect_uri: 'http://localhost:8080/callback', scope: nil, logger: nil, storage: nil) ⇒ OAuthProvider
Initialize OAuth provider
33 34 35 36 37 38 39 40 |
# File 'lib/mcp_client/auth/oauth_provider.rb', line 33 def initialize(server_url:, redirect_uri: 'http://localhost:8080/callback', scope: nil, logger: nil, storage: nil) self.server_url = server_url self.redirect_uri = redirect_uri self.scope = scope self.logger = logger || Logger.new($stdout, level: Logger::WARN) self.storage = storage || MemoryStorage.new @http_client = create_http_client end |
Instance Attribute Details
#logger ⇒ Logger
Returns Logger instance.
24 |
# File 'lib/mcp_client/auth/oauth_provider.rb', line 24 attr_accessor :redirect_uri, :scope, :logger, :storage |
#redirect_uri ⇒ String
Returns OAuth redirect URI.
24 25 26 |
# File 'lib/mcp_client/auth/oauth_provider.rb', line 24 def redirect_uri @redirect_uri end |
#scope ⇒ String?
Returns OAuth scope.
24 |
# File 'lib/mcp_client/auth/oauth_provider.rb', line 24 attr_accessor :redirect_uri, :scope, :logger, :storage |
#server_url ⇒ String
Returns The MCP server URL (normalized).
24 |
# File 'lib/mcp_client/auth/oauth_provider.rb', line 24 attr_accessor :redirect_uri, :scope, :logger, :storage |
#storage ⇒ Object
Returns Storage backend for tokens and client info.
24 |
# File 'lib/mcp_client/auth/oauth_provider.rb', line 24 attr_accessor :redirect_uri, :scope, :logger, :storage |
Instance Method Details
#access_token ⇒ Token?
Get current access token (refresh if needed)
49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/mcp_client/auth/oauth_provider.rb', line 49 def access_token token = storage.get_token(server_url) logger.debug("OAuth access_token: retrieved token=#{token ? 'present' : 'nil'} for #{server_url}") return nil unless token # Return token if still valid return token unless token.expired? || token.expires_soon? # Try to refresh if we have a refresh token refresh_token(token) if token.refresh_token end |
#apply_authorization(request) ⇒ void
This method returns an undefined value.
Apply OAuth authorization to HTTP request
117 118 119 120 121 122 123 124 |
# File 'lib/mcp_client/auth/oauth_provider.rb', line 117 def (request) token = access_token logger.debug("OAuth apply_authorization: token=#{token ? 'present' : 'nil'}") return unless token logger.debug("OAuth applying authorization header: #{token.to_header[0..20]}...") request.headers['Authorization'] = token.to_header end |
#complete_authorization_flow(code, state) ⇒ Token
Complete OAuth authorization flow with authorization code
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/mcp_client/auth/oauth_provider.rb', line 89 def (code, state) # Verify state parameter stored_state = storage.get_state(server_url) raise ArgumentError, 'Invalid state parameter' unless stored_state == state # Get stored PKCE and client info pkce = storage.get_pkce(server_url) client_info = storage.get_client_info(server_url) = raise MCPClient::Errors::ConnectionError, 'Missing PKCE or client info' unless pkce && client_info # Exchange authorization code for tokens token = (, client_info, code, pkce) # Store token storage.set_token(server_url, token) # Clean up temporary data storage.delete_pkce(server_url) storage.delete_state(server_url) token end |
#handle_unauthorized_response(response) ⇒ ResourceMetadata?
Handle 401 Unauthorized response (for server discovery)
129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/mcp_client/auth/oauth_provider.rb', line 129 def (response) www_authenticate = response.headers['WWW-Authenticate'] || response.headers['www-authenticate'] return nil unless www_authenticate # Parse WWW-Authenticate header to extract resource metadata URL # Format: Bearer resource="https://example.com/.well-known/oauth-protected-resource" if (match = www_authenticate.match(/resource="([^"]+)"/)) = match[1] () end end |
#start_authorization_flow ⇒ String
Start OAuth authorization flow
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/mcp_client/auth/oauth_provider.rb', line 64 def # Discover authorization server = # Register client if needed client_info = get_or_register_client() # Generate PKCE parameters pkce = PKCE.new storage.set_pkce(server_url, pkce) # Generate state parameter state = SecureRandom.urlsafe_base64(32) storage.set_state(server_url, state) # Build authorization URL (, client_info, pkce, state) end |