require "test_helper"
+require "jwt"
class OAuth2Test < ActionDispatch::IntegrationTest
def test_oauth2
+ user = create(:user)
client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
state = SecureRandom.urlsafe_base64(16)
- authorize_client(client, :state => state)
+ authorize_client(user, client, :state => state)
assert_response :redirect
code = validate_redirect(client, state)
token = request_token(client, code)
- test_token(token, client)
+ assert_equal "read_prefs", token["scope"]
+ test_token(token["access_token"], user, client)
end
def test_oauth2_oob
+ user = create(:user)
client = create(:oauth_application, :redirect_uri => "urn:ietf:wg:oauth:2.0:oob", :scopes => "read_prefs write_api read_gpx")
- authorize_client(client)
+ authorize_client(user, client)
assert_response :redirect
follow_redirect!
assert_response :success
token = request_token(client, code)
- test_token(token, client)
+ assert_equal "read_prefs", token["scope"]
+ test_token(token["access_token"], user, client)
end
def test_oauth2_pkce_plain
+ user = create(:user)
client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
state = SecureRandom.urlsafe_base64(16)
verifier = SecureRandom.urlsafe_base64(48)
challenge = verifier
- authorize_client(client, :state => state, :code_challenge => challenge, :code_challenge_method => "plain")
+ authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "plain")
assert_response :redirect
code = validate_redirect(client, state)
token = request_token(client, code, verifier)
- test_token(token, client)
+ assert_equal "read_prefs", token["scope"]
+ test_token(token["access_token"], user, client)
end
def test_oauth2_pkce_s256
+ user = create(:user)
client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
state = SecureRandom.urlsafe_base64(16)
verifier = SecureRandom.urlsafe_base64(48)
challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), :padding => false)
- authorize_client(client, :state => state, :code_challenge => challenge, :code_challenge_method => "S256")
+ authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "S256")
assert_response :redirect
code = validate_redirect(client, state)
token = request_token(client, code, verifier)
- test_token(token, client)
+ assert_equal "read_prefs", token["scope"]
+ test_token(token["access_token"], user, client)
+ end
+
+ def test_openid_connect
+ user = create(:user)
+ client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "openid read_prefs")
+ state = SecureRandom.urlsafe_base64(16)
+ verifier = SecureRandom.urlsafe_base64(48)
+ challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), :padding => false)
+
+ authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "S256", :scope => "openid read_prefs")
+ assert_response :redirect
+ code = validate_redirect(client, state)
+
+ token = request_token(client, code, verifier)
+
+ assert_equal "openid read_prefs", token["scope"]
+
+ access_token = token["access_token"]
+ assert_not_nil access_token
+
+ id_token = token["id_token"]
+ assert_not_nil id_token
+
+ data, _headers = JWT.decode id_token, nil, true, {
+ :algorithm => [Doorkeeper::OpenidConnect.signing_algorithm.to_s],
+ :verify_iss => true,
+ :iss => "#{Settings.server_protocol}://#{Settings.server_url}",
+ :verify_sub => true,
+ :sub => user.id,
+ :verify_aud => true,
+ :aud => client.uid
+ } do |headers, _payload|
+ kid = headers["kid"]
+ get oauth_discovery_keys_path
+ keys = response.parsed_body["keys"]
+ jwk = keys&.detect { |e| e["kid"] == kid }
+ jwk && JWT::JWK::RSA.import(jwk).public_key
+ end
+
+ assert_equal user.id.to_s, data["sub"]
+ assert_not data.key?("preferred_username")
+
+ get oauth_userinfo_path
+ assert_response :unauthorized
+
+ auth_header = bearer_authorization_header(access_token)
+ get oauth_userinfo_path, :headers => auth_header
+ assert_response :success
+
+ userinfo = response.parsed_body
+
+ assert_not_nil userinfo
+ assert_equal user.id.to_s, userinfo["sub"]
+ assert_equal user.display_name, userinfo["preferred_username"]
+ end
+
+ def test_openid_discovery
+ get oauth_discovery_provider_path
+ assert_response :success
+ openid_config = response.parsed_body
+
+ assert_equal "#{Settings.server_protocol}://#{Settings.server_url}", openid_config["issuer"]
+
+ assert_equal oauth_authorization_path, URI(openid_config["authorization_endpoint"]).path
+ assert_equal oauth_token_path, URI(openid_config["token_endpoint"]).path
+ assert_equal oauth_userinfo_path, URI(openid_config["userinfo_endpoint"]).path
+ assert_equal oauth_discovery_keys_path, URI(openid_config["jwks_uri"]).path
+ end
+
+ def test_openid_key
+ get oauth_discovery_keys_path
+ assert_response :success
+ key_info = response.parsed_body
+ assert key_info.key?("keys")
+ assert_equal 1, key_info["keys"].size
+ assert_equal Doorkeeper::OpenidConnect.signing_key.kid, key_info["keys"][0]["kid"]
end
private
- def authorize_client(client, options = {})
- options = options.merge(:client_id => client.uid,
- :redirect_uri => client.redirect_uri,
- :response_type => "code",
- :scope => "read_prefs")
+ def authorize_client(user, client, options = {})
+ options = {
+ :client_id => client.uid,
+ :redirect_uri => client.redirect_uri,
+ :response_type => "code",
+ :scope => "read_prefs"
+ }.merge(options)
get oauth_authorization_path(options)
- assert_response :redirect
assert_redirected_to login_path(:referer => request.fullpath)
- user = create(:user)
-
post login_path(:username => user.email, :password => "test")
follow_redirect!
assert_response :success
post oauth_token_path(options)
assert_response :success
- token = JSON.parse(response.body)
+ token = response.parsed_body
assert_equal "Bearer", token["token_type"]
- assert_equal "read_prefs", token["scope"]
- token["access_token"]
+ token
end
- def test_token(token, client)
+ def test_token(token, user, client)
get user_preferences_path
assert_response :unauthorized
get api_trace_path(:id => 2), :headers => auth_header
assert_response :forbidden
+ user.suspend!
+
+ get user_preferences_path, :headers => auth_header
+ assert_response :forbidden
+
+ user.hide!
+
+ get user_preferences_path, :headers => auth_header
+ assert_response :forbidden
+
+ user.unhide!
+
+ get user_preferences_path, :headers => auth_header
+ assert_response :success
+
post oauth_revoke_path(:token => token)
assert_response :forbidden