Module: Mcp::Auth::ProtectedResource
- Extended by:
- ActiveSupport::Concern
- Includes:
- ControllerHelpers
- Defined in:
- lib/mcp/auth/protected_resource.rb
Overview
Resource-server side of the MCP authorization spec. Include this in the controller that serves your MCP endpoint to validate the incoming Bearer token and expose the authenticated principal via Mcp::Auth::ControllerHelpers (mcp_user_id, mcp_scope, …).
class McpController < ApplicationController
include Mcp::Auth::ProtectedResource
before_action :authenticate_mcp_token!
before_action -> { require_mcp_scope!('mcp:read') }, only: :show
end
On a missing/invalid/expired token it answers 401 with the RFC 9728 WWW-Authenticate header so MCP clients can discover the authorization server from the protected-resource metadata.
Instance Method Summary collapse
-
#authenticate_mcp_token! ⇒ Object
Validates the Bearer access token (signature, expiry, revocation status, and — when a resource is configured — the RFC 8707 audience).
-
#require_mcp_scope!(*required) ⇒ Object
Enforce that the validated token carries every given scope.
Methods included from ControllerHelpers
#mcp_api_key, #mcp_authenticated?, #mcp_email, #mcp_org_id, #mcp_scope, #mcp_token, #mcp_user_id
Instance Method Details
#authenticate_mcp_token! ⇒ Object
Validates the Bearer access token (signature, expiry, revocation status, and — when a resource is configured — the RFC 8707 audience). On success the decoded claims are stashed in request.env for ControllerHelpers and the payload is returned; on failure it renders 401 and halts the action.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/mcp/auth/protected_resource.rb', line 27 def authenticate_mcp_token! token = mcp_bearer_token payload = token && Services::TokenService.validate_access_token(token, resource: mcp_resource_identifier) unless payload ('invalid_token', 'The access token is missing, invalid, or expired') return false end request.env['mcp.user_id'] = payload[:sub] request.env['mcp.org_id'] = payload[:org] request.env['mcp.email'] = payload[:email] request.env['mcp.token'] = token request.env['mcp.scope'] = payload[:scope] request.env['mcp.api_key'] = payload[:api_key_id] payload end |
#require_mcp_scope!(*required) ⇒ Object
Enforce that the validated token carries every given scope. Renders 403 insufficient_scope and returns false when any is missing.
47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/mcp/auth/protected_resource.rb', line 47 def require_mcp_scope!(*required) granted = mcp_scope.to_s.split missing = required.map(&:to_s) - granted return true if missing.empty? ( 'insufficient_scope', "Missing required scope: #{missing.join(' ')}", status: :forbidden ) false end |