]> git.openstreetmap.org Git - rails.git/blob - test/integration/oauth2_test.rb
Merge pull request #7302 from tomhughes/terms-locale
[rails.git] / test / integration / oauth2_test.rb
1 # frozen_string_literal: true
2
3 require "test_helper"
4 require "jwt"
5
6 class OAuth2Test < ActionDispatch::IntegrationTest
7   def test_oauth2
8     user = create(:user)
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)
11
12     authorize_client(user, client, :state => state)
13     assert_response :redirect
14     code = validate_redirect(client, state)
15
16     token = request_token(client, code)
17
18     assert_equal "read_prefs", token["scope"]
19     test_token(token["access_token"], user, client)
20   end
21
22   def test_oauth2_oob
23     user = create(:user)
24     client = create(:oauth_application, :redirect_uri => "urn:ietf:wg:oauth:2.0:oob", :scopes => "read_prefs write_api read_gpx")
25
26     authorize_client(user, client)
27     assert_response :redirect
28     follow_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>})
32     assert_not_nil m
33     code = m[1]
34
35     token = request_token(client, code)
36
37     assert_equal "read_prefs", token["scope"]
38     test_token(token["access_token"], user, client)
39   end
40
41   def test_oauth2_pkce_plain
42     user = create(:user)
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)
46     challenge = verifier
47
48     authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "plain")
49     assert_response :redirect
50     code = validate_redirect(client, state)
51
52     token = request_token(client, code, verifier)
53
54     assert_equal "read_prefs", token["scope"]
55     test_token(token["access_token"], user, client)
56   end
57
58   def test_oauth2_pkce_s256
59     user = create(:user)
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)
64
65     authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "S256")
66     assert_response :redirect
67     code = validate_redirect(client, state)
68
69     token = request_token(client, code, verifier)
70
71     assert_equal "read_prefs", token["scope"]
72     test_token(token["access_token"], user, client)
73   end
74
75   def test_openid_connect
76     user = create(:user)
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)
81
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)
85
86     token = request_token(client, code, verifier)
87
88     assert_equal "openid read_prefs", token["scope"]
89
90     access_token = token["access_token"]
91     assert_not_nil access_token
92
93     id_token = token["id_token"]
94     assert_not_nil id_token
95
96     data, _headers = JWT.decode id_token, nil, true, {
97       :algorithm => [Doorkeeper::OpenidConnect.signing_algorithm.to_s],
98       :verify_iss => true,
99       :iss => "#{Settings.server_protocol}://#{Settings.server_url}",
100       :verify_sub => true,
101       :sub => user.id,
102       :verify_aud => true,
103       :aud => client.uid
104     } do |headers, _payload|
105       kid = headers["kid"]
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
110     end
111
112     assert_equal user.id.to_s, data["sub"]
113     assert_equal user.display_name, data["preferred_username"]
114
115     get oauth_userinfo_path
116     assert_response :unauthorized
117
118     auth_header = bearer_authorization_header(access_token)
119     get oauth_userinfo_path, :headers => auth_header
120     assert_response :success
121
122     userinfo = response.parsed_body
123
124     assert_not_nil userinfo
125     assert_equal user.id.to_s, userinfo["sub"]
126     assert_equal user.display_name, userinfo["preferred_username"]
127   end
128
129   def test_openid_discovery
130     get oauth_discovery_provider_path
131     assert_response :success
132     openid_config = response.parsed_body
133
134     assert_equal "#{Settings.server_protocol}://#{Settings.server_url}", openid_config["issuer"]
135
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
140   end
141
142   def test_openid_key
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"]
149   end
150
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")
153
154     options = {
155       :client_id => client.uid,
156       :redirect_uri => client.redirect_uri,
157       :response_type => "code",
158       :scope => "read_prefs"
159     }
160
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"
164     get oauth_path
165     assert_redirected_to login_for_oauth_path
166     get login_for_oauth_path
167     assert_match "Sign Up", response.body
168   end
169
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")
172
173     options = {
174       :client_id => client.uid,
175       :redirect_uri => client.redirect_uri,
176       :response_type => "code",
177       :scope => "read_prefs",
178       :allow_signup => "false"
179     }
180
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"
184     get oauth_path
185     assert_redirected_to login_for_oauth_path
186     get login_for_oauth_path
187     assert_no_match "Sign Up", response.body
188   end
189
190   private
191
192   def authorize_client(user, client, options = {})
193     options = {
194       :client_id => client.uid,
195       :redirect_uri => client.redirect_uri,
196       :response_type => "code",
197       :scope => "read_prefs"
198     }.merge(options)
199
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"
203     get oauth_path
204     assert_redirected_to login_for_oauth_path
205     get login_for_oauth_path
206
207     post login_path(:username => user.email, :password => "s3cr3t")
208     follow_redirect!
209     assert_response :success
210
211     get oauth_authorization_path(options)
212     assert_response :success
213     assert_template "oauth2_authorizations/new"
214
215     delete oauth_authorization_path(options)
216
217     validate_deny(client, options)
218
219     post oauth_authorization_path(options)
220   end
221
222   def validate_deny(client, options)
223     if client.redirect_uri == "urn:ietf:wg:oauth:2.0:oob"
224       assert_response :bad_request
225     else
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"]
233     end
234   end
235
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"]
241
242     query["code"]
243   end
244
245   def request_token(client, code, verifier = nil)
246     options = {
247       :client_id => client.uid,
248       :client_secret => client.plaintext_secret,
249       :code => code,
250       :grant_type => "authorization_code",
251       :redirect_uri => client.redirect_uri
252     }
253
254     if verifier
255       post oauth_token_path(options)
256       assert_response :bad_request
257
258       options = options.merge(:code_verifier => verifier)
259     end
260
261     post oauth_token_path(options)
262     assert_response :success
263     token = response.parsed_body
264     assert_equal "Bearer", token["token_type"]
265
266     token
267   end
268
269   def test_token(token, user, client)
270     get api_user_preferences_path
271     assert_response :unauthorized
272
273     auth_header = bearer_authorization_header(token)
274
275     get api_user_preferences_path, :headers => auth_header
276     assert_response :success
277
278     get api_user_preferences_path(:access_token => token)
279     assert_response :unauthorized
280
281     get api_user_preferences_path(:bearer_token => token)
282     assert_response :unauthorized
283
284     get api_trace_path(:id => 2), :headers => auth_header
285     assert_response :forbidden
286
287     user.suspend!
288
289     get api_user_preferences_path, :headers => auth_header
290     assert_response :forbidden
291
292     user.mark_deleted!
293
294     get api_user_preferences_path, :headers => auth_header
295     assert_response :forbidden
296
297     user.undelete!
298
299     get api_user_preferences_path, :headers => auth_header
300     assert_response :success
301
302     post oauth_revoke_path(:token => token)
303     assert_response :forbidden
304
305     post oauth_revoke_path(:token => token,
306                            :client_id => client.uid,
307                            :client_secret => client.plaintext_secret)
308     assert_response :success
309
310     get api_user_preferences_path, :headers => auth_header
311     assert_response :unauthorized
312   end
313 end