From 1f62a2b3429c2cb9376258b7ae1392ce9a9e660b Mon Sep 17 00:00:00 2001 From: Milan Cvetkovic Date: Fri, 15 Sep 2023 12:24:35 +0000 Subject: [PATCH] Add a test for openid connect support --- Gemfile | 1 + Gemfile.lock | 1 + config/settings/test.yml | 30 +++++++++++++ test/integration/oauth2_test.rb | 74 ++++++++++++++++++++++++++++----- 4 files changed, 96 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index bf65965d4..b738fa159 100644 --- a/Gemfile +++ b/Gemfile @@ -149,6 +149,7 @@ group :test do gem "capybara", ">= 2.15" gem "erb_lint", :require => false gem "factory_bot_rails" + gem "jwt" gem "minitest", "~> 5.1" gem "puma", "~> 5.6" gem "rails-controller-testing" diff --git a/Gemfile.lock b/Gemfile.lock index 2a51a3d36..35d67d327 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -586,6 +586,7 @@ DEPENDENCIES jbuilder (~> 2.7) jquery-rails json + jwt kgio kramdown libxml-ruby (>= 2.0.5) diff --git a/config/settings/test.yml b/config/settings/test.yml index 5f0025925..0cfa74cd7 100644 --- a/config/settings/test.yml +++ b/config/settings/test.yml @@ -22,3 +22,33 @@ trace_icon_storage: "test" # Lower some rate limits for testing max_changeset_comments_per_hour: 30 moderator_changeset_comments_per_hour: 60 +# Private key for signing id_tokens +doorkeeper_signing_key: | + -----BEGIN PRIVATE KEY----- + MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDA4pSRHIicerJQ + BvIy9lGJ6ZQA7SAGVM8QeNMBaQftS+ROMY+6CFCJ0kiwb9oDdtFNyo3gpgmlULMC + q0C96r0UllKnTSkHSntkKM0wy3TX0pa8QBaJbbcOXU13xu5cR7ffvtn2kQX8RZc9 + eZtE/bSybNBDSiS4mbP31cSQ71EYsbfD3UiWOpOTbc6Xlw9kCkUjwXk36Jnim7gF + 1kFjD3Vq94ij4OVNxcFp+btrfhq2tsiXa9IPBlt1xTetHwj5HrxseDu2qNQNgPxY + ivFAA3t4BuIuou1HjAdzfqp7Ylsr2b7qx+w+Y9TqhH6AcKd0i1qxh6bYnJezH9JU + BjlJvJMhAgMBAAECggEAAID2/gldiqRqunkc1n48iJ2CufKPRAT3r3rT+OvNzf8F + 6csJAuWKVE8ndR0trBb6L/eooYloJWA4aiLes0BIMyQQs4go5HB7hwTw7ZYycsKF + i0NS676iHO2odKN2iZN/CvIO1AKH9KM35GdgvPA5XG1FU/pUbeOqNn+pQ5mkaWnt + kt+ndBpJQDPSS7nTY8g2BCh97SJSbxEPAccAqNLSvKQED4QVygC63jYZNPDxkJWI + guzNn4wv1AfM0DU4W5fI0UtNSxcWSsefWBJTOKO/uQr/XJglxVh6uKof1dnBZiJD + KU6/+bR1cXoKQ05HAcEcf/mtjJGwnze41p1EI22gYQKBgQDB+VZJwvxlME1MgEGJ + WFPPKiQspKjS0kgbfBw7Iny+mYM6YLpQyF0NFNRloALW2rHH2QLNSerHMlytZUAd + 1SluQZ4We6P3hLDi2J3p37lkIdBXhjJi8gdoEfQ1YVcCbPGbR2ZVwYms7BP3yiQY + ZLcHLUKPKG6hOZztY1gBYqoKqQKBgQD+kBtR8krdJHPEU3m+d/6NWlGk4KZgCFx5 + ouN/aHtxE6Ge+mUwbrJun/oVrFjbX7ySYTYYb6SdKUrchyKfJL4Z89WHGwrFTV1/ + 6J2ShXmoeUeic1TS4btcnFmZyCXlADk1eyHZm9wtkwd5e2lBfdRxzErKC42lWdaQ + rreP2nZHuQKBgQCiNbVgB6vznrn1kIe9qFylsJMBtkzryCe+vEILfaKd7VhdOEh2 + h6ew6ctYlL/rFoV3H1YFgJvSKp5v7mz4xapY5oyiNpD+yzr06LrdulaZkuFcX//A + 2K8y61iyTw1pHNvKw6Gjcy6DqgRkwej/cTHR0ZqIhwJE1x4RMnOE7RJPyQKBgQCM + SLYFjtSa0b/KbYYl5NKu6xsbFYIaYgE0NwPP7rA4PG1QwwSIkDhcpmSXFQdSvYuZ + z2CUTtIUmfDbXs1BjmoEu07syYZB/MSN/I75c/z7TvqfF5ejLyqlerQV/yqC7ICa + bGTXGwFXTDNOSyhSIxm0LLT6ayt/9+Y6jU4zRFzyYQKBgGiScevkv/XNz9MXswJ+ + 2bEIJNIJn0wIeuopifcDQrOTeCK+037t1AQ3lxMXisJABwG1jfw7WTjF3zz4dSUX + cK1+/2V+OkM/0nXjxPwPj7LiOediUyZNUn48r29uGOL1S83PSUdyST207CP6mZjc + K8aJmnGsVEAcWPzbpNh14q/c + -----END PRIVATE KEY----- diff --git a/test/integration/oauth2_test.rb b/test/integration/oauth2_test.rb index 81f12f7cb..b7c6f3a34 100644 --- a/test/integration/oauth2_test.rb +++ b/test/integration/oauth2_test.rb @@ -1,4 +1,5 @@ require "test_helper" +require "jwt" class OAuth2Test < ActionDispatch::IntegrationTest def test_oauth2 @@ -12,7 +13,8 @@ class OAuth2Test < ActionDispatch::IntegrationTest token = request_token(client, code) - test_token(token, user, client) + assert_equal "read_prefs", token["scope"] + test_token(token["access_token"], user, client) end def test_oauth2_oob @@ -30,7 +32,8 @@ class OAuth2Test < ActionDispatch::IntegrationTest token = request_token(client, code) - test_token(token, user, client) + assert_equal "read_prefs", token["scope"] + test_token(token["access_token"], user, client) end def test_oauth2_pkce_plain @@ -46,7 +49,8 @@ class OAuth2Test < ActionDispatch::IntegrationTest token = request_token(client, code, verifier) - test_token(token, user, client) + assert_equal "read_prefs", token["scope"] + test_token(token["access_token"], user, client) end def test_oauth2_pkce_s256 @@ -62,16 +66,67 @@ class OAuth2Test < ActionDispatch::IntegrationTest token = request_token(client, code, verifier) - test_token(token, user, 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, Doorkeeper::OpenidConnect.signing_key.keypair, 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 + } + + 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 private def authorize_client(user, client, options = {}) - options = options.merge(:client_id => client.uid, - :redirect_uri => client.redirect_uri, - :response_type => "code", - :scope => "read_prefs") + 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 @@ -135,9 +190,8 @@ class OAuth2Test < ActionDispatch::IntegrationTest assert_response :success 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, user, client) -- 2.45.1