]> git.openstreetmap.org Git - rails.git/commitdiff
Add a test for openid connect support
authorMilan Cvetkovic <mcvetkovic@microsoft.com>
Fri, 15 Sep 2023 12:24:35 +0000 (12:24 +0000)
committerTom Hughes <tom@compton.nu>
Tue, 3 Oct 2023 17:55:10 +0000 (18:55 +0100)
Gemfile
Gemfile.lock
config/settings/test.yml
test/integration/oauth2_test.rb

diff --git a/Gemfile b/Gemfile
index bf65965d458d7bb78d406ea956663e6849988b69..b738fa1592bae068fb49312e2cd5971fdc7c4c6d 100644 (file)
--- 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 "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"
   gem "minitest", "~> 5.1"
   gem "puma", "~> 5.6"
   gem "rails-controller-testing"
index 2a51a3d367bc5a840d7a929e193cc55be5dca263..35d67d32763a5aa5d880b811a6859219ff274246 100644 (file)
@@ -586,6 +586,7 @@ DEPENDENCIES
   jbuilder (~> 2.7)
   jquery-rails
   json
   jbuilder (~> 2.7)
   jquery-rails
   json
+  jwt
   kgio
   kramdown
   libxml-ruby (>= 2.0.5)
   kgio
   kramdown
   libxml-ruby (>= 2.0.5)
index 5f00259256d6ef81873ad323810657d9ce289c06..0cfa74cd70738ca851244a4830bf2b7bb0ed63fe 100644 (file)
@@ -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
 # 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-----
index 81f12f7cbef8feb3b635d1085495c4c460ec9bdf..b7c6f3a34a1967e981cc6ce49de59db0fd0f27df 100644 (file)
@@ -1,4 +1,5 @@
 require "test_helper"
 require "test_helper"
+require "jwt"
 
 class OAuth2Test < ActionDispatch::IntegrationTest
   def test_oauth2
 
 class OAuth2Test < ActionDispatch::IntegrationTest
   def test_oauth2
@@ -12,7 +13,8 @@ class OAuth2Test < ActionDispatch::IntegrationTest
 
     token = request_token(client, code)
 
 
     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
   end
 
   def test_oauth2_oob
@@ -30,7 +32,8 @@ class OAuth2Test < ActionDispatch::IntegrationTest
 
     token = request_token(client, code)
 
 
     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
   end
 
   def test_oauth2_pkce_plain
@@ -46,7 +49,8 @@ class OAuth2Test < ActionDispatch::IntegrationTest
 
     token = request_token(client, code, verifier)
 
 
     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
   end
 
   def test_oauth2_pkce_s256
@@ -62,16 +66,67 @@ class OAuth2Test < ActionDispatch::IntegrationTest
 
     token = request_token(client, code, verifier)
 
 
     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 = {})
   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
 
     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_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)
   end
 
   def test_token(token, user, client)