4 class OAuth2Test < ActionDispatch::IntegrationTest
 
   7     client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
 
   8     state = SecureRandom.urlsafe_base64(16)
 
  10     authorize_client(user, client, :state => state)
 
  11     assert_response :redirect
 
  12     code = validate_redirect(client, state)
 
  14     token = request_token(client, code)
 
  16     assert_equal "read_prefs", token["scope"]
 
  17     test_token(token["access_token"], user, client)
 
  22     client = create(:oauth_application, :redirect_uri => "urn:ietf:wg:oauth:2.0:oob", :scopes => "read_prefs write_api read_gpx")
 
  24     authorize_client(user, client)
 
  25     assert_response :redirect
 
  27     assert_response :success
 
  28     assert_template "oauth2_authorizations/show"
 
  29     m = response.body.match(%r{<code id="authorization_code">([A-Za-z0-9_-]+)</code>})
 
  33     token = request_token(client, code)
 
  35     assert_equal "read_prefs", token["scope"]
 
  36     test_token(token["access_token"], user, client)
 
  39   def test_oauth2_pkce_plain
 
  41     client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
 
  42     state = SecureRandom.urlsafe_base64(16)
 
  43     verifier = SecureRandom.urlsafe_base64(48)
 
  46     authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "plain")
 
  47     assert_response :redirect
 
  48     code = validate_redirect(client, state)
 
  50     token = request_token(client, code, verifier)
 
  52     assert_equal "read_prefs", token["scope"]
 
  53     test_token(token["access_token"], user, client)
 
  56   def test_oauth2_pkce_s256
 
  58     client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
 
  59     state = SecureRandom.urlsafe_base64(16)
 
  60     verifier = SecureRandom.urlsafe_base64(48)
 
  61     challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), :padding => false)
 
  63     authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "S256")
 
  64     assert_response :redirect
 
  65     code = validate_redirect(client, state)
 
  67     token = request_token(client, code, verifier)
 
  69     assert_equal "read_prefs", token["scope"]
 
  70     test_token(token["access_token"], user, client)
 
  73   def test_openid_connect
 
  75     client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "openid read_prefs")
 
  76     state = SecureRandom.urlsafe_base64(16)
 
  77     verifier = SecureRandom.urlsafe_base64(48)
 
  78     challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), :padding => false)
 
  80     authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "S256", :scope => "openid read_prefs")
 
  81     assert_response :redirect
 
  82     code = validate_redirect(client, state)
 
  84     token = request_token(client, code, verifier)
 
  86     assert_equal "openid read_prefs", token["scope"]
 
  88     access_token = token["access_token"]
 
  89     assert_not_nil access_token
 
  91     id_token = token["id_token"]
 
  92     assert_not_nil id_token
 
  94     data, _headers = JWT.decode id_token, nil, true, {
 
  95       :algorithm => [Doorkeeper::OpenidConnect.signing_algorithm.to_s],
 
  97       :iss => "#{Settings.server_protocol}://#{Settings.server_url}",
 
 102     } do |headers, _payload|
 
 104       get oauth_discovery_keys_path
 
 105       keys = response.parsed_body["keys"]
 
 106       jwk = keys&.detect { |e| e["kid"] == kid }
 
 107       jwk && JWT::JWK::RSA.import(jwk).public_key
 
 110     assert_equal user.id.to_s, data["sub"]
 
 111     assert_not data.key?("preferred_username")
 
 113     get oauth_userinfo_path
 
 114     assert_response :unauthorized
 
 116     auth_header = bearer_authorization_header(access_token)
 
 117     get oauth_userinfo_path, :headers => auth_header
 
 118     assert_response :success
 
 120     userinfo = response.parsed_body
 
 122     assert_not_nil userinfo
 
 123     assert_equal user.id.to_s, userinfo["sub"]
 
 124     assert_equal user.display_name, userinfo["preferred_username"]
 
 127   def test_openid_discovery
 
 128     get oauth_discovery_provider_path
 
 129     assert_response :success
 
 130     openid_config = response.parsed_body
 
 132     assert_equal "#{Settings.server_protocol}://#{Settings.server_url}", openid_config["issuer"]
 
 134     assert_equal oauth_authorization_path, URI(openid_config["authorization_endpoint"]).path
 
 135     assert_equal oauth_token_path, URI(openid_config["token_endpoint"]).path
 
 136     assert_equal oauth_userinfo_path, URI(openid_config["userinfo_endpoint"]).path
 
 137     assert_equal oauth_discovery_keys_path, URI(openid_config["jwks_uri"]).path
 
 141     get oauth_discovery_keys_path
 
 142     assert_response :success
 
 143     key_info = response.parsed_body
 
 144     assert key_info.key?("keys")
 
 145     assert_equal 1, key_info["keys"].size
 
 146     assert_equal Doorkeeper::OpenidConnect.signing_key.kid, key_info["keys"][0]["kid"]
 
 151   def authorize_client(user, client, options = {})
 
 153       :client_id => client.uid,
 
 154       :redirect_uri => client.redirect_uri,
 
 155       :response_type => "code",
 
 156       :scope => "read_prefs"
 
 159     get oauth_authorization_path(options)
 
 160     assert_redirected_to login_path(:referer => request.fullpath)
 
 162     post login_path(:username => user.email, :password => "test")
 
 164     assert_response :success
 
 166     get oauth_authorization_path(options)
 
 167     assert_response :success
 
 168     assert_template "oauth2_authorizations/new"
 
 170     delete oauth_authorization_path(options)
 
 172     validate_deny(client, options)
 
 174     post oauth_authorization_path(options)
 
 177   def validate_deny(client, options)
 
 178     if client.redirect_uri == "urn:ietf:wg:oauth:2.0:oob"
 
 179       assert_response :bad_request
 
 181       assert_response :redirect
 
 182       location = URI.parse(response.location)
 
 183       assert_match(/^#{Regexp.escape(client.redirect_uri)}/, location.to_s)
 
 184       query = Rack::Utils.parse_query(location.query)
 
 185       assert_equal "access_denied", query["error"]
 
 186       assert_equal "The resource owner or authorization server denied the request.", query["error_description"]
 
 187       assert_equal options[:state], query["state"]
 
 191   def validate_redirect(client, state)
 
 192     location = URI.parse(response.location)
 
 193     assert_match(/^#{Regexp.escape(client.redirect_uri)}/, location.to_s)
 
 194     query = Rack::Utils.parse_query(location.query)
 
 195     assert_equal state, query["state"]
 
 200   def request_token(client, code, verifier = nil)
 
 202       :client_id => client.uid,
 
 203       :client_secret => client.plaintext_secret,
 
 205       :grant_type => "authorization_code",
 
 206       :redirect_uri => client.redirect_uri
 
 210       post oauth_token_path(options)
 
 211       assert_response :bad_request
 
 213       options = options.merge(:code_verifier => verifier)
 
 216     post oauth_token_path(options)
 
 217     assert_response :success
 
 218     token = response.parsed_body
 
 219     assert_equal "Bearer", token["token_type"]
 
 224   def test_token(token, user, client)
 
 225     get api_user_preferences_path
 
 226     assert_response :unauthorized
 
 228     auth_header = bearer_authorization_header(token)
 
 230     get api_user_preferences_path, :headers => auth_header
 
 231     assert_response :success
 
 233     get api_user_preferences_path(:access_token => token)
 
 234     assert_response :unauthorized
 
 236     get api_user_preferences_path(:bearer_token => token)
 
 237     assert_response :unauthorized
 
 239     get api_trace_path(:id => 2), :headers => auth_header
 
 240     assert_response :forbidden
 
 244     get api_user_preferences_path, :headers => auth_header
 
 245     assert_response :forbidden
 
 249     get api_user_preferences_path, :headers => auth_header
 
 250     assert_response :forbidden
 
 254     get api_user_preferences_path, :headers => auth_header
 
 255     assert_response :success
 
 257     post oauth_revoke_path(:token => token)
 
 258     assert_response :forbidden
 
 260     post oauth_revoke_path(:token => token,
 
 261                            :client_id => client.uid,
 
 262                            :client_secret => client.plaintext_secret)
 
 263     assert_response :success
 
 265     get api_user_preferences_path, :headers => auth_header
 
 266     assert_response :unauthorized