Module: SMARTAppLaunch::TokenRequestVerification

Instance Method Summary collapse

Instance Method Details

#check_authorization_code_request_params(params, request_num) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/smart_app_launch/client_suite/token_request_verification.rb', line 46

def check_authorization_code_request_params(params, request_num)
  if params['code'].present?

    authorization_request = MockSMARTServer.authorization_request_for_code(params['code'], test_session_id)

    if authorization_request.present?
      authorization_body = MockSMARTServer.authorization_code_request_details(authorization_request)

      if params['redirect_uri'] != authorization_body['redirect_uri']
        add_message('error', "Authorization code token request #{request_num} included an incorrect " \
                             "`redirect_uri` value: expected '#{authorization_body['redirect_uri']} " \
                             "but got '#{params['redirect_uri']}'")
      end

      pkce_error = MockSMARTServer.pkce_error(params['code_verifier'],
                                              authorization_body['code_challenge'],
                                              authorization_body['code_challenge_method'])
      if pkce_error.present?
        add_message('error', 'Error performing pkce verification on the `code_verifier` value in ' \
                             "authorization code token request #{request_num}: #{pkce_error}")
      end
    else
      add_message('error', "Authorization code token request #{request_num} included a code not " \
                           "issued during this test session: '#{params['code']}'")
    end
  else
    add_message('error', "Authorization code token request #{request_num} missing a `code`")
  end
end

#check_refresh_request_params(params, oauth_flow, authentication_approach, request_num) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/smart_app_launch/client_suite/token_request_verification.rb', line 76

def check_refresh_request_params(params, oauth_flow, authentication_approach, request_num)
  if oauth_flow == CLIENT_CREDENTIALS_TAG
    add_message('error',
                "Invalid refresh request #{request_num} found during client_credentials flow.")
    return
  end
  
  if params['grant_type'] != 'refresh_token'
    add_message('error',
                "Refresh request #{request_num} had an incorrect `grant_type`: expected 'refresh_token', " \
                "but got '#{params['grant_type']}'")
  end
  if authentication_approach == CONFIDENTIAL_ASYMMETRIC_TAG && 
      params['client_assertion_type'] != 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
    add_message('error',
                "Confidential asymmetric refresh request #{request_num} had an incorrect `client_assertion_type`: " \
                "expected 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', " \
                "but got '#{params['client_assertion_type']}'")
  end

  authorization_code = MockSMARTServer.refresh_token_to_authorization_code(params['refresh_token'])
  authorization_request = MockSMARTServer.authorization_request_for_code(authorization_code, test_session_id)
  if authorization_request.present?
    # todo - check that the scope is a subset of the original authorization code request
  else
    add_message('error', "Authorization code token refresh request #{request_num} included a refresh token not " \
                         "issued during this test session: '#{params['refresh_token']}'")
  end

  nil
end

#check_request_params(params, oauth_flow, authentication_approach, request_num) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/smart_app_launch/client_suite/token_request_verification.rb', line 20

def check_request_params(params, oauth_flow, authentication_approach, request_num)
  if params['grant_type'] != oauth_flow
    add_message('error',
                "Token request #{request_num} had an incorrect `grant_type`: expected #{oauth_flow}, " \
                "but got '#{params['grant_type']}'")
  end
  if authentication_approach == CONFIDENTIAL_ASYMMETRIC_TAG && 
     params['client_assertion_type'] != 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
    add_message('error',
                "Confidential asymmetric token request #{request_num} had an incorrect `client_assertion_type`: " \
                "expected 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', " \
                "but got '#{params['client_assertion_type']}'")
  end
  if oauth_flow == CLIENT_CREDENTIALS_TAG && params['scope'].blank?
    add_message('error', "Client credentials token request #{request_num} did not include the requested `scope`")
  end
  if authentication_approach == PUBLIC_TAG && params['client_id'] != client_id
    add_message('error', "Public client token request #{request_num} had an incorrect `client` value: " \
                         "expected '#{client_id}' but got '#{params['client_id']}'")
  end

  check_authorization_code_request_params(params, request_num) if oauth_flow == AUTHORIZATION_CODE_TAG

  nil
end

#extract_token_from_response(request) ⇒ Object



108
109
110
111
112
113
114
# File 'lib/smart_app_launch/client_suite/token_request_verification.rb', line 108

def extract_token_from_response(request)
  return unless request.status == 200

  JSON.parse(request.response_body)&.dig('access_token')
rescue
  nil
end

#verify_token_requests(oauth_flow, authentication_approach) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/smart_app_launch/client_suite/token_request_verification.rb', line 4

def verify_token_requests(oauth_flow, authentication_approach)
  jti_list = []
  token_list = []
  requests.each_with_index do |token_request, index|
    request_params = URI.decode_www_form(token_request.request_body).to_h
    request_params['grant_type'] != 'refresh_token' ?
      check_request_params(request_params, oauth_flow, authentication_approach, index + 1) :
      check_refresh_request_params(request_params, oauth_flow, authentication_approach, index + 1)
    check_authentication(authentication_approach, token_request, request_params, jti_list, index + 1)
    
    token_list << extract_token_from_response(token_request)
  end

  output smart_tokens: token_list.compact.join("\n")
end