Module: BetterAuth::Plugins::OAuthProvider::MCP

Defined in:
lib/better_auth/plugins/oauth_provider/mcp.rb

Class Method Summary collapse

Class Method Details

.handle_mcp_errors(error, resource, resource_metadata_mappings: {}) ⇒ Object

Raises:

  • (APIError)


41
42
43
44
45
46
47
48
49
# File 'lib/better_auth/plugins/oauth_provider/mcp.rb', line 41

def handle_mcp_errors(error, resource, resource_metadata_mappings: {})
  raise error unless error.is_a?(APIError) && error.status_code == 401

  raise APIError.new(
    "UNAUTHORIZED",
    message: error.message,
    headers: {"WWW-Authenticate" => www_authenticate(resource, resource_metadata_mappings: )}
  )
end

.mcp_handler(resource:, verifier:, resource_metadata_mappings: {}, &handler) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/better_auth/plugins/oauth_provider/mcp.rb', line 51

def mcp_handler(resource:, verifier:, resource_metadata_mappings: {}, &handler)
  lambda do |request|
    authorization = request.respond_to?(:headers) ? request.headers["authorization"] : nil
    token = authorization.to_s.delete_prefix("Bearer ").strip
    raise APIError.new("UNAUTHORIZED", message: "missing authorization header") if token.empty?

    jwt = verifier.call(token)
    handler.call(request, jwt)
  rescue APIError => error
    handle_mcp_errors(error, resource, resource_metadata_mappings: )
  end
end

.resource_metadata_url(resource, mappings = {}) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/better_auth/plugins/oauth_provider/mcp.rb', line 16

def (resource, mappings = {})
  value = resource.to_s
  uri = URI.parse(value)
  if uri.scheme && uri.host
    path = uri.path.to_s.end_with?("/") ? uri.path.to_s.delete_suffix("/") : uri.path.to_s
    return "#{resource_origin(uri)}/.well-known/oauth-protected-resource#{path}"
  end

  mapped = OAuthProtocol.stringify_keys(mappings || {})[value] || mappings[value.to_sym]
  raise APIError.new("INTERNAL_SERVER_ERROR", message: "missing resource_metadata mapping for #{value}") if mapped.to_s.empty?

  mapped
rescue URI::InvalidURIError
  mapped = OAuthProtocol.stringify_keys(mappings || {})[value] || mappings[value.to_sym]
  raise APIError.new("INTERNAL_SERVER_ERROR", message: "missing resource_metadata mapping for #{value}") if mapped.to_s.empty?

  mapped
end

.resource_origin(uri) ⇒ Object



35
36
37
38
39
# File 'lib/better_auth/plugins/oauth_provider/mcp.rb', line 35

def resource_origin(uri)
  default_port = (uri.scheme == "http" && uri.port == 80) || (uri.scheme == "https" && uri.port == 443)
  port = default_port ? "" : ":#{uri.port}"
  "#{uri.scheme}://#{uri.host}#{port}"
end

.www_authenticate(resource, resource_metadata_mappings: {}) ⇒ Object



9
10
11
12
13
14
# File 'lib/better_auth/plugins/oauth_provider/mcp.rb', line 9

def www_authenticate(resource, resource_metadata_mappings: {})
  Array(resource).map do |value|
     = (value, )
    %(Bearer resource_metadata="#{}")
  end.join(", ")
end