]> git.openstreetmap.org Git - rails.git/blob - test/integration/oauth2_test.rb
Merge remote-tracking branch 'upstream/pull/3856'
[rails.git] / test / integration / oauth2_test.rb
1 require "test_helper"
2
3 class OAuth2Test < ActionDispatch::IntegrationTest
4   def test_oauth2
5     client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
6     state = SecureRandom.urlsafe_base64(16)
7
8     authorize_client(client, :state => state)
9     assert_response :redirect
10     code = validate_redirect(client, state)
11
12     token = request_token(client, code)
13
14     test_token(token, client)
15   end
16
17   def test_oauth2_oob
18     client = create(:oauth_application, :redirect_uri => "urn:ietf:wg:oauth:2.0:oob", :scopes => "read_prefs write_api read_gpx")
19
20     authorize_client(client)
21     assert_response :redirect
22     follow_redirect!
23     assert_response :success
24     assert_template "oauth2_authorizations/show"
25     m = response.body.match(%r{<code id="authorization_code">([A-Za-z0-9_-]+)</code>})
26     assert_not_nil m
27     code = m[1]
28
29     token = request_token(client, code)
30
31     test_token(token, client)
32   end
33
34   def test_oauth2_pkce_plain
35     client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
36     state = SecureRandom.urlsafe_base64(16)
37     verifier = SecureRandom.urlsafe_base64(48)
38     challenge = verifier
39
40     authorize_client(client, :state => state, :code_challenge => challenge, :code_challenge_method => "plain")
41     assert_response :redirect
42     code = validate_redirect(client, state)
43
44     token = request_token(client, code, verifier)
45
46     test_token(token, client)
47   end
48
49   def test_oauth2_pkce_s256
50     client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
51     state = SecureRandom.urlsafe_base64(16)
52     verifier = SecureRandom.urlsafe_base64(48)
53     challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), :padding => false)
54
55     authorize_client(client, :state => state, :code_challenge => challenge, :code_challenge_method => "S256")
56     assert_response :redirect
57     code = validate_redirect(client, state)
58
59     token = request_token(client, code, verifier)
60
61     test_token(token, client)
62   end
63
64   private
65
66   def authorize_client(client, options = {})
67     options = options.merge(:client_id => client.uid,
68                             :redirect_uri => client.redirect_uri,
69                             :response_type => "code",
70                             :scope => "read_prefs")
71
72     get oauth_authorization_path(options)
73     assert_response :redirect
74     assert_redirected_to login_path(:referer => request.fullpath)
75
76     user = create(:user)
77
78     post login_path(:username => user.email, :password => "test")
79     follow_redirect!
80     assert_response :success
81
82     get oauth_authorization_path(options)
83     assert_response :success
84     assert_template "oauth2_authorizations/new"
85
86     delete oauth_authorization_path(options)
87
88     validate_deny(client, options)
89
90     post oauth_authorization_path(options)
91   end
92
93   def validate_deny(client, options)
94     if client.redirect_uri == "urn:ietf:wg:oauth:2.0:oob"
95       assert_response :bad_request
96     else
97       assert_response :redirect
98       location = URI.parse(response.location)
99       assert_match(/^#{Regexp.escape(client.redirect_uri)}/, location.to_s)
100       query = Rack::Utils.parse_query(location.query)
101       assert_equal "access_denied", query["error"]
102       assert_equal "The resource owner or authorization server denied the request.", query["error_description"]
103       assert_equal options[:state], query["state"]
104     end
105   end
106
107   def validate_redirect(client, state)
108     location = URI.parse(response.location)
109     assert_match(/^#{Regexp.escape(client.redirect_uri)}/, location.to_s)
110     query = Rack::Utils.parse_query(location.query)
111     assert_equal state, query["state"]
112
113     query["code"]
114   end
115
116   def request_token(client, code, verifier = nil)
117     options = {
118       :client_id => client.uid,
119       :client_secret => client.plaintext_secret,
120       :code => code,
121       :grant_type => "authorization_code",
122       :redirect_uri => client.redirect_uri
123     }
124
125     if verifier
126       post oauth_token_path(options)
127       assert_response :bad_request
128
129       options = options.merge(:code_verifier => verifier)
130     end
131
132     post oauth_token_path(options)
133     assert_response :success
134     token = JSON.parse(response.body)
135     assert_equal "Bearer", token["token_type"]
136     assert_equal "read_prefs", token["scope"]
137
138     token["access_token"]
139   end
140
141   def test_token(token, client)
142     get user_preferences_path
143     assert_response :unauthorized
144
145     auth_header = bearer_authorization_header(token)
146
147     get user_preferences_path, :headers => auth_header
148     assert_response :success
149
150     get user_preferences_path(:access_token => token)
151     assert_response :unauthorized
152
153     get user_preferences_path(:bearer_token => token)
154     assert_response :unauthorized
155
156     get api_trace_path(:id => 2), :headers => auth_header
157     assert_response :forbidden
158
159     post oauth_revoke_path(:token => token)
160     assert_response :forbidden
161
162     post oauth_revoke_path(:token => token,
163                            :client_id => client.uid,
164                            :client_secret => client.plaintext_secret)
165     assert_response :success
166
167     get user_preferences_path, :headers => auth_header
168     assert_response :unauthorized
169   end
170 end