/
tokens_controller_spec.rb
330 lines (246 loc) · 10.8 KB
/
tokens_controller_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# frozen_string_literal: true
require "spec_helper"
describe Doorkeeper::TokensController do
describe "when authorization has succeeded" do
let(:token) { double(:token, authorize: true) }
it "returns the authorization" do
skip "verify need of these specs"
expect(token).to receive(:authorization)
post :create
end
end
describe "when authorization has failed" do
it "returns the error response" do
token = double(:token, authorize: false)
allow(controller).to receive(:token) { token }
post :create
expect(response.status).to eq 400
expect(response.headers["WWW-Authenticate"]).to match(/Bearer/)
end
end
describe "when there is a failure due to a custom error" do
it "returns the error response with a custom message" do
# I18n looks for `doorkeeper.errors.messages.custom_message` in locale files
custom_message = "my_message"
allow(I18n).to receive(:translate)
.with(
custom_message,
hash_including(scope: %i[doorkeeper errors messages])
)
.and_return("Authorization custom message")
doorkeeper_error = Doorkeeper::Errors::DoorkeeperError.new(custom_message)
strategy = double(:strategy)
request = double(token_request: strategy)
allow(strategy).to receive(:authorize).and_raise(doorkeeper_error)
allow(controller).to receive(:server).and_return(request)
post :create
expected_response_body = {
"error" => custom_message,
"error_description" => "Authorization custom message",
}
expect(response.status).to eq 400
expect(response.headers["WWW-Authenticate"]).to match(/Bearer/)
expect(JSON.parse(response.body)).to eq expected_response_body
end
end
# http://tools.ietf.org/html/rfc7009#section-2.2
describe "revoking tokens" do
let(:client) { FactoryBot.create(:application) }
let(:access_token) { FactoryBot.create(:access_token, application: client) }
context "when associated app is public" do
let(:client) { FactoryBot.create(:application, confidential: false) }
it "returns 200" do
post :revoke, params: { token: access_token.token }
expect(response.status).to eq 200
end
it "revokes the access token" do
post :revoke, params: { token: access_token.token }
expect(access_token.reload).to have_attributes(revoked?: true)
end
end
context "when associated app is confidential" do
let(:client) { FactoryBot.create(:application, confidential: true) }
let(:oauth_client) { Doorkeeper::OAuth::Client.new(client) }
before(:each) do
allow_any_instance_of(Doorkeeper::Server).to receive(:client) { oauth_client }
end
it "returns 200" do
post :revoke, params: { token: access_token.token }
expect(response.status).to eq 200
end
it "revokes the access token" do
post :revoke, params: { token: access_token.token }
expect(access_token.reload).to have_attributes(revoked?: true)
end
context "when authorization fails" do
let(:some_other_client) { FactoryBot.create(:application, confidential: true) }
let(:oauth_client) { Doorkeeper::OAuth::Client.new(some_other_client) }
it "returns 200" do
post :revoke, params: { token: access_token.token }
expect(response.status).to eq 200
end
it "does not revoke the access token" do
post :revoke, params: { token: access_token.token }
expect(access_token.reload).to have_attributes(revoked?: false)
end
end
end
end
describe "authorize response memoization" do
it "memoizes the result of the authorization" do
strategy = double(:strategy, authorize: true)
expect(strategy).to receive(:authorize).once
allow(controller).to receive(:strategy) { strategy }
allow(controller).to receive(:create) do
2.times { controller.send :authorize_response }
controller.render json: {}, status: :ok
end
post :create
end
end
describe "when requested token introspection" do
let(:client) { FactoryBot.create(:application) }
let(:access_token) { FactoryBot.create(:access_token, application: client) }
let(:token_for_introspection) { FactoryBot.create(:access_token, application: client) }
context "authorized using valid Bearer token" do
it "responds with full token introspection" do
request.headers["Authorization"] = "Bearer #{access_token.token}"
post :introspect, params: { token: token_for_introspection.token }
should_have_json "active", true
expect(json_response).to include("client_id", "token_type", "exp", "iat")
end
end
context "authorized using valid Client Authentication" do
it "responds with full token introspection" do
request.headers["Authorization"] = basic_auth_header_for_client(client)
post :introspect, params: { token: token_for_introspection.token }
should_have_json "active", true
expect(json_response).to include("client_id", "token_type", "exp", "iat")
should_have_json "client_id", client.uid
end
end
context "using custom introspection response" do
before do
Doorkeeper.configure do
orm DOORKEEPER_ORM
custom_introspection_response do |_token, _context|
{
sub: "Z5O3upPC88QrAjx00dis",
aud: "https://protected.example.net/resource",
}
end
end
end
it "responds with full token introspection" do
request.headers["Authorization"] = "Bearer #{access_token.token}"
post :introspect, params: { token: token_for_introspection.token }
expect(json_response).to include("client_id", "token_type", "exp", "iat", "sub", "aud")
should_have_json "sub", "Z5O3upPC88QrAjx00dis"
should_have_json "aud", "https://protected.example.net/resource"
end
end
context "public access token" do
let(:token_for_introspection) { FactoryBot.create(:access_token, application: nil) }
it "responds with full token introspection" do
request.headers["Authorization"] = basic_auth_header_for_client(client)
post :introspect, params: { token: token_for_introspection.token }
should_have_json "active", true
expect(json_response).to include("client_id", "token_type", "exp", "iat")
should_have_json "client_id", nil
end
end
context "token was issued to a different client than is making this request" do
let(:different_client) { FactoryBot.create(:application) }
it "responds with only active state" do
request.headers["Authorization"] = basic_auth_header_for_client(different_client)
post :introspect, params: { token: token_for_introspection.token }
expect(response).to be_successful
should_have_json "active", false
expect(json_response).not_to include("client_id", "token_type", "exp", "iat")
end
end
context "authorized using invalid Bearer token" do
let(:access_token) do
FactoryBot.create(:access_token, application: client, revoked_at: 1.day.ago)
end
it "responds with invalid token error" do
request.headers["Authorization"] = "Bearer #{access_token.token}"
post :introspect, params: { token: token_for_introspection.token }
response_status_should_be 401
should_not_have_json "active"
should_have_json "error", "invalid_token"
end
end
context "authorized using the Bearer token that need to be introspected" do
it "responds with invalid token error" do
request.headers["Authorization"] = "Bearer #{access_token.token}"
post :introspect, params: { token: access_token.token }
response_status_should_be 401
should_not_have_json "active"
should_have_json "error", "invalid_token"
end
end
context "using invalid credentials to authorize" do
let(:client) { double(uid: "123123", secret: "666999") }
let(:access_token) { FactoryBot.create(:access_token) }
it "responds with invalid_client error" do
request.headers["Authorization"] = basic_auth_header_for_client(client)
post :introspect, params: { token: access_token.token }
expect(response).not_to be_successful
response_status_should_be 401
should_not_have_json "active"
should_have_json "error", "invalid_client"
end
end
context "using wrong token value" do
context "authorized using client credentials" do
it "responds with only active state" do
request.headers["Authorization"] = basic_auth_header_for_client(client)
post :introspect, params: { token: SecureRandom.hex(16) }
should_have_json "active", false
expect(json_response).not_to include("client_id", "token_type", "exp", "iat")
end
end
context "authorized using valid Bearer token" do
it "responds with only active state" do
request.headers["Authorization"] = "Bearer #{access_token.token}"
post :introspect, params: { token: SecureRandom.hex(16) }
should_have_json "active", false
expect(json_response).not_to include("client_id", "token_type", "exp", "iat")
end
end
end
context "when requested access token expired" do
let(:token_for_introspection) do
FactoryBot.create(:access_token, application: client, created_at: 1.year.ago)
end
it "responds with only active state" do
request.headers["Authorization"] = basic_auth_header_for_client(client)
post :introspect, params: { token: token_for_introspection.token }
should_have_json "active", false
expect(json_response).not_to include("client_id", "token_type", "exp", "iat")
end
end
context "when requested Access Token revoked" do
let(:token_for_introspection) do
FactoryBot.create(:access_token, application: client, revoked_at: 1.year.ago)
end
it "responds with only active state" do
request.headers["Authorization"] = basic_auth_header_for_client(client)
post :introspect, params: { token: token_for_introspection.token }
should_have_json "active", false
expect(json_response).not_to include("client_id", "token_type", "exp", "iat")
end
end
context "unauthorized (no bearer token or client credentials)" do
let(:token_for_introspection) { FactoryBot.create(:access_token) }
it "responds with invalid_request error" do
post :introspect, params: { token: token_for_introspection.token }
expect(response).not_to be_successful
response_status_should_be 400
should_not_have_json "active"
should_have_json "error", "invalid_request"
end
end
end
end