4 class OAuth2Test < ActionDispatch::IntegrationTest
7 client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
8 state = SecureRandom.urlsafe_base64(16)
10 authorize_client(user, client, :state => state)
11 assert_response :redirect
12 code = validate_redirect(client, state)
14 token = request_token(client, code)
16 assert_equal "read_prefs", token["scope"]
17 test_token(token["access_token"], user, client)
22 client = create(:oauth_application, :redirect_uri => "urn:ietf:wg:oauth:2.0:oob", :scopes => "read_prefs write_api read_gpx")
24 authorize_client(user, client)
25 assert_response :redirect
27 assert_response :success
28 assert_template "oauth2_authorizations/show"
29 m = response.body.match(%r{<code id="authorization_code">([A-Za-z0-9_-]+)</code>})
33 token = request_token(client, code)
35 assert_equal "read_prefs", token["scope"]
36 test_token(token["access_token"], user, client)
39 def test_oauth2_pkce_plain
41 client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
42 state = SecureRandom.urlsafe_base64(16)
43 verifier = SecureRandom.urlsafe_base64(48)
46 authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "plain")
47 assert_response :redirect
48 code = validate_redirect(client, state)
50 token = request_token(client, code, verifier)
52 assert_equal "read_prefs", token["scope"]
53 test_token(token["access_token"], user, client)
56 def test_oauth2_pkce_s256
58 client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "read_prefs write_api read_gpx")
59 state = SecureRandom.urlsafe_base64(16)
60 verifier = SecureRandom.urlsafe_base64(48)
61 challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), :padding => false)
63 authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "S256")
64 assert_response :redirect
65 code = validate_redirect(client, state)
67 token = request_token(client, code, verifier)
69 assert_equal "read_prefs", token["scope"]
70 test_token(token["access_token"], user, client)
73 def test_openid_connect
75 client = create(:oauth_application, :redirect_uri => "https://some.web.app.example.org/callback", :scopes => "openid read_prefs")
76 state = SecureRandom.urlsafe_base64(16)
77 verifier = SecureRandom.urlsafe_base64(48)
78 challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), :padding => false)
80 authorize_client(user, client, :state => state, :code_challenge => challenge, :code_challenge_method => "S256", :scope => "openid read_prefs")
81 assert_response :redirect
82 code = validate_redirect(client, state)
84 token = request_token(client, code, verifier)
86 assert_equal "openid read_prefs", token["scope"]
88 access_token = token["access_token"]
89 assert_not_nil access_token
91 id_token = token["id_token"]
92 assert_not_nil id_token
94 data, _headers = JWT.decode id_token, Doorkeeper::OpenidConnect.signing_key.public_key, true, {
95 :algorithm => [Doorkeeper::OpenidConnect.signing_algorithm.to_s],
97 :iss => "#{Settings.server_protocol}://#{Settings.server_url}",
104 assert_equal user.id.to_s, data["sub"]
105 assert_not data.key?("preferred_username")
107 get oauth_userinfo_path
108 assert_response :unauthorized
110 auth_header = bearer_authorization_header(access_token)
111 get oauth_userinfo_path, :headers => auth_header
112 assert_response :success
114 userinfo = response.parsed_body
116 assert_not_nil userinfo
117 assert_equal user.id.to_s, userinfo["sub"]
118 assert_equal user.display_name, userinfo["preferred_username"]
121 def test_openid_discovery
122 get oauth_discovery_provider_path
123 assert_response :success
124 openid_config = response.parsed_body
126 assert_equal "#{Settings.server_protocol}://#{Settings.server_url}", openid_config["issuer"]
128 assert_equal oauth_authorization_path, URI(openid_config["authorization_endpoint"]).path
129 assert_equal oauth_token_path, URI(openid_config["token_endpoint"]).path
130 assert_equal oauth_userinfo_path, URI(openid_config["userinfo_endpoint"]).path
131 assert_equal oauth_discovery_keys_path, URI(openid_config["jwks_uri"]).path
135 get oauth_discovery_keys_path
136 assert_response :success
137 key_info = response.parsed_body
138 assert key_info.key?("keys")
139 assert_equal 1, key_info["keys"].size
140 assert_equal Doorkeeper::OpenidConnect.signing_key.kid, key_info["keys"][0]["kid"]
145 def authorize_client(user, client, options = {})
147 :client_id => client.uid,
148 :redirect_uri => client.redirect_uri,
149 :response_type => "code",
150 :scope => "read_prefs"
153 get oauth_authorization_path(options)
154 assert_response :redirect
155 assert_redirected_to login_path(:referer => request.fullpath)
157 post login_path(:username => user.email, :password => "test")
159 assert_response :success
161 get oauth_authorization_path(options)
162 assert_response :success
163 assert_template "oauth2_authorizations/new"
165 delete oauth_authorization_path(options)
167 validate_deny(client, options)
169 post oauth_authorization_path(options)
172 def validate_deny(client, options)
173 if client.redirect_uri == "urn:ietf:wg:oauth:2.0:oob"
174 assert_response :bad_request
176 assert_response :redirect
177 location = URI.parse(response.location)
178 assert_match(/^#{Regexp.escape(client.redirect_uri)}/, location.to_s)
179 query = Rack::Utils.parse_query(location.query)
180 assert_equal "access_denied", query["error"]
181 assert_equal "The resource owner or authorization server denied the request.", query["error_description"]
182 assert_equal options[:state], query["state"]
186 def validate_redirect(client, state)
187 location = URI.parse(response.location)
188 assert_match(/^#{Regexp.escape(client.redirect_uri)}/, location.to_s)
189 query = Rack::Utils.parse_query(location.query)
190 assert_equal state, query["state"]
195 def request_token(client, code, verifier = nil)
197 :client_id => client.uid,
198 :client_secret => client.plaintext_secret,
200 :grant_type => "authorization_code",
201 :redirect_uri => client.redirect_uri
205 post oauth_token_path(options)
206 assert_response :bad_request
208 options = options.merge(:code_verifier => verifier)
211 post oauth_token_path(options)
212 assert_response :success
213 token = response.parsed_body
214 assert_equal "Bearer", token["token_type"]
219 def test_token(token, user, client)
220 get user_preferences_path
221 assert_response :unauthorized
223 auth_header = bearer_authorization_header(token)
225 get user_preferences_path, :headers => auth_header
226 assert_response :success
228 get user_preferences_path(:access_token => token)
229 assert_response :unauthorized
231 get user_preferences_path(:bearer_token => token)
232 assert_response :unauthorized
234 get api_trace_path(:id => 2), :headers => auth_header
235 assert_response :forbidden
239 get user_preferences_path, :headers => auth_header
240 assert_response :forbidden
244 get user_preferences_path, :headers => auth_header
245 assert_response :forbidden
249 get user_preferences_path, :headers => auth_header
250 assert_response :success
252 post oauth_revoke_path(:token => token)
253 assert_response :forbidden
255 post oauth_revoke_path(:token => token,
256 :client_id => client.uid,
257 :client_secret => client.plaintext_secret)
258 assert_response :success
260 get user_preferences_path, :headers => auth_header
261 assert_response :unauthorized