Class: Mcp::Auth::Services::AuthorizationService

Inherits:
Object
  • Object
show all
Defined in:
lib/mcp/auth/services/authorization_service.rb

Class Method Summary collapse

Class Method Details

.consume_authorization_code(code) ⇒ Object

Consume authorization code (one-time use)



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/mcp/auth/services/authorization_service.rb', line 56

def consume_authorization_code(code)
  authorization_code = Mcp::Auth::AuthorizationCode.find_by(code: code)
  return nil unless authorization_code

  code_data = {
    client_id: authorization_code.client_id,
    redirect_uri: authorization_code.redirect_uri,
    code_challenge: authorization_code.code_challenge,
    code_challenge_method: authorization_code.code_challenge_method,
    resource: authorization_code.resource,
    scope: authorization_code.scope,
    user_id: authorization_code.user_id,
    org_id: authorization_code.org_id,
    created_at: authorization_code.created_at.to_i
  }

  authorization_code.destroy
  Rails.logger.info "[AuthorizationService] Authorization code consumed"
  code_data
end

.generate_authorization_code(params, user:, org:) ⇒ Object

Generate authorization code with PKCE support



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/mcp/auth/services/authorization_service.rb', line 9

def generate_authorization_code(params, user:, org:)
  code = SecureRandom.hex(32)

  # Use provided scope or default to all registered scopes
  scope = params[:scope].presence || Mcp::Auth::ScopeRegistry.default_scope_string

  authorization_code = Mcp::Auth::AuthorizationCode.create!(
    code: code,
    client_id: params[:client_id],
    redirect_uri: params[:redirect_uri],
    code_challenge: params[:code_challenge],
    code_challenge_method: params[:code_challenge_method],
    resource: params[:resource],
    scope: scope,
    user: user,
    org: org,
    expires_at: authorization_code_lifetime.minutes.from_now
  )

  Rails.logger.info "[AuthorizationService] Authorization code generated for user #{user.id}"
  authorization_code.code
rescue ActiveRecord::RecordInvalid => e
  Rails.logger.error "[AuthorizationService] Failed to create authorization code: #{e.message}"
  nil
end

.validate_authorization_code(code) ⇒ Object

Validate authorization code without consuming it



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/mcp/auth/services/authorization_service.rb', line 36

def validate_authorization_code(code)
  return nil if code.blank?

  authorization_code = Mcp::Auth::AuthorizationCode.active.find_by(code: code)
  return nil unless authorization_code

  {
    client_id: authorization_code.client_id,
    redirect_uri: authorization_code.redirect_uri,
    code_challenge: authorization_code.code_challenge,
    code_challenge_method: authorization_code.code_challenge_method,
    resource: authorization_code.resource,
    scope: authorization_code.scope,
    user_id: authorization_code.user_id,
    org_id: authorization_code.org_id,
    created_at: authorization_code.created_at.to_i
  }
end

.validate_pkce?(code_challenge, code_verifier) ⇒ Boolean

Validate PKCE challenge (RFC 7636)

Returns:

  • (Boolean)


78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/mcp/auth/services/authorization_service.rb', line 78

def validate_pkce?(code_challenge, code_verifier)
  return false if code_verifier.blank? || code_challenge.blank?

  # S256 method: BASE64URL(SHA256(code_verifier))
  computed_challenge = Base64.urlsafe_encode64(
    Digest::SHA256.digest(code_verifier),
    padding: false
  )

  ActiveSupport::SecurityUtils.secure_compare(computed_challenge, code_challenge)
rescue StandardError => e
  Rails.logger.error "[AuthorizationService] PKCE validation error: #{e.message}"
  false
end