Class: RailsAiBridge::Mcp::Authenticator
- Inherits:
-
Object
- Object
- RailsAiBridge::Mcp::Authenticator
- Defined in:
- lib/rails_ai_bridge/mcp/authenticator.rb
Overview
Service object that authenticates HTTP MCP requests.
Consolidates strategy resolution, static-token lookup, and configuration
predicates into a single entry point — previously split across
McpHttpAuth (utility helpers) and Mcp::HttpAuth (strategy orchestrator),
both removed in v2.0.0.
Strategy priority (highest → lowest)
- Configuration#mcp_jwt_decoder → RailsAiBridge::Mcp::Auth::Strategies::Jwt
- Configuration#mcp_token_resolver → RailsAiBridge::Mcp::Auth::Strategies::BearerToken (resolver mode)
config.http_mcp_token/ENV["RAILS_AI_BRIDGE_MCP_TOKEN"]→ RailsAiBridge::Mcp::Auth::Strategies::BearerToken (static mode)- None configured → open access (RailsAiBridge::Mcp::AuthResult.ok)
Security design notes
-
Static-token comparison uses
Digest::SHA256pre-hashing +ActiveSupport::SecurityUtils.secure_compare(constant-time over fixed-length digests). This prevents token-length leakage but does NOT protect against brute-force guessing. Useconfig.mcp.rate_limit_max_requestsfor built-in per-IP rate limiting, orrack-attackfor distributed/stricter quotas. -
The JWT strategy is decoder-only — this gem carries no JWT dependency. The host supplies a lambda; any
StandardErrorit raises is caught and surfaced as:decode_error, never propagated.
Constant Summary collapse
- TOKEN_ENV_KEY =
'RAILS_AI_BRIDGE_MCP_TOKEN'
Class Method Summary collapse
-
.any_configured? ⇒ Boolean
Returns
truewhen any auth mechanism is configured — static token, resolver, or JWT decoder. -
.call(request) ⇒ AuthResult
Authenticate an incoming Rack request against the configured strategy.
-
.strategy_name(strategy) ⇒ String
Human-readable strategy name for observability payloads.
-
.unauthorized_rack_response ⇒ Array<(Integer, Hash{String => String}, Array<String>)>
Builds a Rack 401 response for unauthenticated HTTP MCP requests.
Class Method Details
.any_configured? ⇒ Boolean
Returns true when any auth mechanism is configured — static token,
resolver, or JWT decoder. Used by production-safety validators to confirm
the MCP endpoint is protected.
Checks configuration presence, not runtime correctness. A resolver that
always returns nil still counts as "configured."
71 72 73 |
# File 'lib/rails_ai_bridge/mcp/authenticator.rb', line 71 def any_configured? resolve_strategy.present? end |
.call(request) ⇒ AuthResult
Authenticate an incoming Rack request against the configured strategy.
50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/rails_ai_bridge/mcp/authenticator.rb', line 50 def call(request) strategy = resolve_strategy return AuthResult.ok(nil) if strategy.nil? strategy.authenticate(request).tap do |result| if result.success? Instrumentation.instrument('auth.success', strategy: strategy_name(strategy), ip: request.ip) else Instrumentation.instrument('auth.failure', strategy: strategy_name(strategy), ip: request.ip, error: result.error) end end end |
.strategy_name(strategy) ⇒ String
Human-readable strategy name for observability payloads.
93 94 95 |
# File 'lib/rails_ai_bridge/mcp/authenticator.rb', line 93 def strategy_name(strategy) strategy.class.name&.split('::')&.last || 'unknown' end |
.unauthorized_rack_response ⇒ Array<(Integer, Hash{String => String}, Array<String>)>
Builds a Rack 401 response for unauthenticated HTTP MCP requests.
78 79 80 81 82 83 84 85 86 87 |
# File 'lib/rails_ai_bridge/mcp/authenticator.rb', line 78 def [ 401, { 'Content-Type' => 'application/json', 'WWW-Authenticate' => 'Bearer realm="rails-ai-bridge-mcp"' }, ['{"error":"Unauthorized"}'] ] end |