1 # frozen_string_literal: true
6 class OAuth2Test < ActionDispatch::IntegrationTest
9 client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
10 state = SecureRandom.urlsafe_base64(16)
12 authorize_client(user, client, :state => state)
13 assert_response :redirect
14 code = validate_redirect(client, state)
16 token = request_token(client, code)
18 assert_equal "read_prefs", token["scope"]
19 test_token(token["access_token"], user, client)
24 client = create(:oauth_application, :redirect_uri => "urn:ietf:wg:oauth:2.0:oob", :scopes => "read_prefs write_api read_gpx")
26 authorize_client(user, client)
27 assert_response :redirect
29 assert_response :success
30 assert_template "oauth2_authorizations/show"
31 m = response.body.match(%r{<code id="authorization_code">([A-Za-z0-9_-]+)</code>})
35 token = request_token(client, code)
37 assert_equal "read_prefs", token["scope"]
38 test_token(token["access_token"], user, client)
41 def test_oauth2_pkce_plain
43 client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
44 state = SecureRandom.urlsafe_base64(16)
45 verifier = SecureRandom.urlsafe_base64(48)
48 authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "plain")
49 assert_response :redirect
50 code = validate_redirect(client, state)
52 token = request_token(client, code, verifier)
54 assert_equal "read_prefs", token["scope"]
55 test_token(token["access_token"], user, client)
58 def test_oauth2_pkce_s256
60 client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
61 state = SecureRandom.urlsafe_base64(16)
62 verifier = SecureRandom.urlsafe_base64(48)
63 challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), :padding => false)
65 authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "S256")
66 assert_response :redirect
67 code = validate_redirect(client, state)
69 token = request_token(client, code, verifier)
71 assert_equal "read_prefs", token["scope"]
72 test_token(token["access_token"], user, client)
75 def test_openid_connect
77 client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "openid read_prefs")
78 state = SecureRandom.urlsafe_base64(16)
79 verifier = SecureRandom.urlsafe_base64(48)
80 challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), :padding => false)
82 authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "S256", :scope => "openid read_prefs")
83 assert_response :redirect
84 code = validate_redirect(client, state)
86 token = request_token(client, code, verifier)
88 assert_equal "openid read_prefs", token["scope"]
90 access_token = token["access_token"]
91 assert_not_nil access_token
93 id_token = token["id_token"]
94 assert_not_nil id_token
96 data, _headers = JWT.decode id_token, nil, true, {
97 :algorithm => [Doorkeeper::OpenidConnect.signing_algorithm.to_s],
99 :iss => "#{Settings.server_protocol}://#{Settings.server_url}",
104 } do |headers, _payload|
106 get oauth_discovery_keys_path
107 keys = response.parsed_body["keys"]
108 jwk = keys&.detect { |e| e["kid"] == kid }
109 jwk && JWT::JWK::RSA.import(jwk).public_key
112 assert_equal user.id.to_s, data["sub"]
113 assert_equal user.display_name, data["preferred_username"]
115 get oauth_userinfo_path
116 assert_response :unauthorized
118 auth_header = bearer_authorization_header(access_token)
119 get oauth_userinfo_path, :headers => auth_header
120 assert_response :success
122 userinfo = response.parsed_body
124 assert_not_nil userinfo
125 assert_equal user.id.to_s, userinfo["sub"]
126 assert_equal user.display_name, userinfo["preferred_username"]
129 def test_openid_discovery
130 get oauth_discovery_provider_path
131 assert_response :success
132 openid_config = response.parsed_body
134 assert_equal "#{Settings.server_protocol}://#{Settings.server_url}", openid_config["issuer"]
136 assert_equal oauth_authorization_path, URI(openid_config["authorization_endpoint"]).path
137 assert_equal oauth_token_path, URI(openid_config["token_endpoint"]).path
138 assert_equal oauth_userinfo_path, URI(openid_config["userinfo_endpoint"]).path
139 assert_equal oauth_discovery_keys_path, URI(openid_config["jwks_uri"]).path
143 get oauth_discovery_keys_path
144 assert_response :success
145 key_info = response.parsed_body
146 assert key_info.key?("keys")
147 assert_equal 1, key_info["keys"].size
148 assert_equal Doorkeeper::OpenidConnect.signing_key.kid, key_info["keys"][0]["kid"]
151 def test_allow_signup_not_set
152 client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
155 :client_id => client.uid,
156 :redirect_uri => client.redirect_uri,
157 :response_type => "code",
158 :scope => "read_prefs"
161 oauth_path = oauth_authorization_path(options)
162 login_for_oauth_path = login_path(:referer => oauth_path)
163 cookies["_osm_session"] = "reassure the backend that cookies are enabled"
165 assert_redirected_to login_for_oauth_path
166 get login_for_oauth_path
167 assert_match "Sign Up", response.body
170 def test_allow_signup_false
171 client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
174 :client_id => client.uid,
175 :redirect_uri => client.redirect_uri,
176 :response_type => "code",
177 :scope => "read_prefs",
178 :allow_signup => "false"
181 oauth_path = oauth_authorization_path(options)
182 login_for_oauth_path = login_path(:referer => oauth_path)
183 cookies["_osm_session"] = "reassure the backend that cookies are enabled"
185 assert_redirected_to login_for_oauth_path
186 get login_for_oauth_path
187 assert_no_match "Sign Up", response.body
192 def authorize_client(user, client, options = {})
194 :client_id => client.uid,
195 :redirect_uri => client.redirect_uri,
196 :response_type => "code",
197 :scope => "read_prefs"
200 oauth_path = oauth_authorization_path(options)
201 login_for_oauth_path = login_path(:referer => oauth_path)
202 cookies["_osm_session"] = "reassure the backend that cookies are enabled"
204 assert_redirected_to login_for_oauth_path
205 get login_for_oauth_path
207 post login_path(:username => user.email, :password => "s3cr3t")
209 assert_response :success
211 get oauth_authorization_path(options)
212 assert_response :success
213 assert_template "oauth2_authorizations/new"
215 delete oauth_authorization_path(options)
217 validate_deny(client, options)
219 post oauth_authorization_path(options)
222 def validate_deny(client, options)
223 if client.redirect_uri == "urn:ietf:wg:oauth:2.0:oob"
224 assert_response :bad_request
226 assert_response :redirect
227 location = URI.parse(response.location)
228 assert_match(/^#{Regexp.escape(client.redirect_uri)}/, location.to_s)
229 query = Rack::Utils.parse_query(location.query)
230 assert_equal "access_denied", query["error"]
231 assert_equal "The resource owner or authorization server denied the request.", query["error_description"]
232 assert_equal options[:state], query["state"]
236 def validate_redirect(client, state)
237 location = URI.parse(response.location)
238 assert_match(/^#{Regexp.escape(client.redirect_uri)}/, location.to_s)
239 query = Rack::Utils.parse_query(location.query)
240 assert_equal state, query["state"]
245 def request_token(client, code, verifier = nil)
247 :client_id => client.uid,
248 :client_secret => client.plaintext_secret,
250 :grant_type => "authorization_code",
251 :redirect_uri => client.redirect_uri
255 post oauth_token_path(options)
256 assert_response :bad_request
258 options = options.merge(:code_verifier => verifier)
261 post oauth_token_path(options)
262 assert_response :success
263 token = response.parsed_body
264 assert_equal "Bearer", token["token_type"]
269 def test_token(token, user, client)
270 get api_user_preferences_path
271 assert_response :unauthorized
273 auth_header = bearer_authorization_header(token)
275 get api_user_preferences_path, :headers => auth_header
276 assert_response :success
278 get api_user_preferences_path(:access_token => token)
279 assert_response :unauthorized
281 get api_user_preferences_path(:bearer_token => token)
282 assert_response :unauthorized
284 get api_trace_path(:id => 2), :headers => auth_header
285 assert_response :forbidden
289 get api_user_preferences_path, :headers => auth_header
290 assert_response :forbidden
294 get api_user_preferences_path, :headers => auth_header
295 assert_response :forbidden
299 get api_user_preferences_path, :headers => auth_header
300 assert_response :success
302 post oauth_revoke_path(:token => token)
303 assert_response :forbidden
305 post oauth_revoke_path(:token => token,
306 :client_id => client.uid,
307 :client_secret => client.plaintext_secret)
308 assert_response :success
310 get api_user_preferences_path, :headers => auth_header
311 assert_response :unauthorized