Class: Keycardai::MCP::AuthProvider

Inherits:
Object
  • Object
show all
Defined in:
lib/keycardai/mcp/auth_provider.rb

Overview

The delegated-access provider for an MCP server: holds the zone and the application credential, builds the inbound TokenVerifier, and exposes grant middleware plus imperative token exchange. Multi-zone is inferred from a multi-zone credential (the credential is self-describing).

provider = Keycardai::MCP::AuthProvider.new(
zone_url: "https://acme.keycard.cloud",
credential: Keycardai::OAuth::ClientSecret.new(client_id, client_secret),
)
use Keycardai::MCP::RequireBearerAuth, verifier: provider.token_verifier
use provider.grant("https://api.example.com")

Defined Under Namespace

Classes: Grant

Instance Method Summary collapse

Constructor Details

#initialize(zone_url:, credential: nil, client_id: nil, client_secret: nil, audiences: nil, http_client: Keycardai::OAuth::HTTP::NetHTTPClient.new, timeout: nil) ⇒ AuthProvider

Returns a new instance of AuthProvider.

Parameters:

  • zone_url (String)

    the zone's issuer URL

  • credential (Object, nil) (defaults to: nil)

    an application credential; a multi-zone ClientSecret switches the provider to multi-zone

  • client_id (String, nil) (defaults to: nil)

    shared-secret pair alternative

  • client_secret (String, nil) (defaults to: nil)
  • audiences (String, Array<String>, nil) (defaults to: nil)

    enforced on inbound tokens

  • http_client (#get, #post_form) (defaults to: Keycardai::OAuth::HTTP::NetHTTPClient.new)

    pluggable transport

  • timeout (Numeric, nil) (defaults to: nil)


25
26
27
28
29
30
31
32
33
# File 'lib/keycardai/mcp/auth_provider.rb', line 25

def initialize(zone_url:, credential: nil, client_id: nil, client_secret: nil, audiences: nil,
               http_client: Keycardai::OAuth::HTTP::NetHTTPClient.new, timeout: nil)
  @zone_url = zone_url
  @credential = credential || (client_id ? Keycardai::OAuth::ClientSecret.new(client_id, client_secret) : nil)
  @audiences = audiences
  @http_client = http_client
  @timeout = timeout
  @mutex = Mutex.new
end

Instance Method Details

#exchange_tokens(subject_token, resources, access_context: Keycardai::OAuth::AccessContext.new, user_identifier: nil, request_scopes: nil, issuer: nil) ⇒ Keycardai::OAuth::AccessContext

Imperative exchange: the caller's token for one token per resource.

Returns:

  • (Keycardai::OAuth::AccessContext)


80
81
82
83
84
85
86
87
# File 'lib/keycardai/mcp/auth_provider.rb', line 80

def exchange_tokens(subject_token, resources, access_context: Keycardai::OAuth::AccessContext.new,
                    user_identifier: nil, request_scopes: nil, issuer: nil)
  Keycardai::OAuth.exchange_tokens_for_resources(
    client: exchange_client, resources: Array(resources), subject_token: subject_token,
    access_context: access_context, user_identifier: user_identifier,
    request_scopes: request_scopes, issuer: issuer
  )
end

#exchange_tokens_for_zone(issuer, subject_token, resources) ⇒ Keycardai::OAuth::AccessContext

Zone-selected imperative exchange for multi-zone providers.

Returns:

  • (Keycardai::OAuth::AccessContext)


92
93
94
# File 'lib/keycardai/mcp/auth_provider.rb', line 92

def exchange_tokens_for_zone(issuer, subject_token, resources, **)
  exchange_tokens(subject_token, resources, issuer: issuer, **)
end

#grant(resources, user_identifier: nil, request_scopes: nil) ⇒ Class

Grant middleware: declare the downstream resources a route needs, and the caller's verified token is exchanged for one token per resource before the handler runs. Results and per-resource errors land on the AccessContext in the Rack env (Keycardai::MCP.access_context); a per-resource failure never aborts the request. Stacked grants merge into one context. A missing verified token is rejected 401 fail-fast.

Parameters:

  • resources (String, Array<String>)
  • user_identifier (String, #call, nil) (defaults to: nil)

    impersonation target; a callable receives the Rack env and returns the user id

  • request_scopes (String, Hash{String => String}, nil) (defaults to: nil)

Returns:

  • (Class)

    a Rack middleware class for use



67
68
69
70
71
72
73
74
75
# File 'lib/keycardai/mcp/auth_provider.rb', line 67

def grant(resources, user_identifier: nil, request_scopes: nil)
  provider = self
  Class.new(Grant) do
    define_method(:initialize) do |app|
      super(app, provider: provider, resources: Array(resources),
                 user_identifier: user_identifier, request_scopes: request_scopes)
    end
  end
end

#issuersArray<String>

The issuers this provider trusts: the zone URL, plus every zone of a multi-zone credential.

Returns:

  • (Array<String>)


39
40
41
42
# File 'lib/keycardai/mcp/auth_provider.rb', line 39

def issuers
  zones = @credential.respond_to?(:multi_zone?) && @credential.multi_zone? ? @credential.issuers : []
  ([@zone_url] + zones).uniq
end

#token_verifierKeycardai::OAuth::TokenVerifier

The inbound bearer-token verifier for this provider's zone(s).

Returns:

  • (Keycardai::OAuth::TokenVerifier)


47
48
49
50
51
52
53
# File 'lib/keycardai/mcp/auth_provider.rb', line 47

def token_verifier
  @mutex.synchronize do
    @token_verifier ||= Keycardai::OAuth::TokenVerifier.new(
      issuers: issuers, audiences: @audiences, http_client: @http_client
    )
  end
end