simple_oauth2 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +25 -0
  3. data/.coveralls.yml +1 -0
  4. data/.gitignore +26 -0
  5. data/.hound.yml +4 -0
  6. data/.rubocop.yml +5 -0
  7. data/.rubocop_todo.yml +12 -0
  8. data/.travis.yml +31 -0
  9. data/Gemfile +18 -0
  10. data/LICENSE +21 -0
  11. data/README.md +11 -0
  12. data/Rakefile +11 -0
  13. data/gemfiles/nobrainer.rb +15 -0
  14. data/lib/simple_oauth2/configuration/class_accessors.rb +36 -0
  15. data/lib/simple_oauth2/configuration/constants.rb +36 -0
  16. data/lib/simple_oauth2/configuration.rb +169 -0
  17. data/lib/simple_oauth2/generators/authorization.rb +64 -0
  18. data/lib/simple_oauth2/generators/base.rb +31 -0
  19. data/lib/simple_oauth2/generators/token.rb +71 -0
  20. data/lib/simple_oauth2/helpers.rb +40 -0
  21. data/lib/simple_oauth2/mixins/nobrainer/access_grant.rb +62 -0
  22. data/lib/simple_oauth2/mixins/nobrainer/access_token.rb +98 -0
  23. data/lib/simple_oauth2/mixins/nobrainer/client.rb +43 -0
  24. data/lib/simple_oauth2/resource/bearer.rb +20 -0
  25. data/lib/simple_oauth2/responses.rb +62 -0
  26. data/lib/simple_oauth2/scopes.rb +59 -0
  27. data/lib/simple_oauth2/strategies/authorization_code.rb +22 -0
  28. data/lib/simple_oauth2/strategies/base.rb +61 -0
  29. data/lib/simple_oauth2/strategies/client_credentials.rb +21 -0
  30. data/lib/simple_oauth2/strategies/code.rb +25 -0
  31. data/lib/simple_oauth2/strategies/password.rb +21 -0
  32. data/lib/simple_oauth2/strategies/refresh_token.rb +53 -0
  33. data/lib/simple_oauth2/strategies/token.rb +24 -0
  34. data/lib/simple_oauth2/uniq_token.rb +20 -0
  35. data/lib/simple_oauth2/version.rb +26 -0
  36. data/lib/simple_oauth2.rb +62 -0
  37. data/logo.png +0 -0
  38. data/simple_oauth2.gemspec +22 -0
  39. data/spec/configuration/config_spec.rb +181 -0
  40. data/spec/configuration/version_spec.rb +11 -0
  41. data/spec/dummy/endpoints/authorization.rb +15 -0
  42. data/spec/dummy/endpoints/custom_authorization.rb +21 -0
  43. data/spec/dummy/endpoints/custom_token.rb +21 -0
  44. data/spec/dummy/endpoints/status.rb +51 -0
  45. data/spec/dummy/endpoints/token.rb +22 -0
  46. data/spec/dummy/orm/nobrainer/app/config/db.rb +8 -0
  47. data/spec/dummy/orm/nobrainer/app/models/access_grant.rb +3 -0
  48. data/spec/dummy/orm/nobrainer/app/models/access_token.rb +3 -0
  49. data/spec/dummy/orm/nobrainer/app/models/client.rb +3 -0
  50. data/spec/dummy/orm/nobrainer/app/models/user.rb +11 -0
  51. data/spec/dummy/orm/nobrainer/app/twitter.rb +51 -0
  52. data/spec/dummy/orm/nobrainer/config.ru +37 -0
  53. data/spec/dummy/simple_oauth2_config.rb +7 -0
  54. data/spec/requests/flows/authorization_code_spec.rb +177 -0
  55. data/spec/requests/flows/client_credentials_spec.rb +163 -0
  56. data/spec/requests/flows/code_spec.rb +98 -0
  57. data/spec/requests/flows/password_spec.rb +183 -0
  58. data/spec/requests/flows/refresh_token_spec.rb +282 -0
  59. data/spec/requests/flows/token_spec.rb +113 -0
  60. data/spec/requests/protected_resources_spec.rb +65 -0
  61. data/spec/requests/revoke_token_spec.rb +90 -0
  62. data/spec/spec_helper.rb +51 -0
  63. data/spec/support/helper.rb +11 -0
  64. metadata +125 -0
@@ -0,0 +1,282 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Token Endpoint' do
4
+ subject { -> { post url, params } }
5
+
6
+ let(:url) { '/oauth/token' }
7
+ let(:client) { Client.create(name: FFaker::Internet.domain_word, redirect_uri: 'http://localhost:3000/home') }
8
+ let(:user) { User.create(username: FFaker::Internet.user_name, encrypted_password: FFaker::Internet.password) }
9
+ let(:client_id) { client.key }
10
+ let(:client_secret) { client.secret }
11
+ let(:grant_type) { 'refresh_token' }
12
+ let(:scopes) { nil }
13
+ let(:access_token) { AccessToken.create_for(client, user, scopes) }
14
+ let(:refresh_token) { access_token.refresh_token }
15
+ let(:params) do
16
+ {
17
+ client_id: client_id,
18
+ client_secret: client_secret,
19
+ refresh_token: refresh_token,
20
+ grant_type: grant_type,
21
+ scope: scopes
22
+ }
23
+ end
24
+
25
+ describe 'POST /oauth/token' do
26
+ describe 'RefreshToken flow' do
27
+ context 'when valid params' do
28
+ context 'return a new Access Token' do
29
+ before do
30
+ allow(Simple::OAuth2.config).to receive(:issue_refresh_token).and_return(true)
31
+ subject.call
32
+ end
33
+
34
+ it { expect(last_response.status).to eq 200 }
35
+
36
+ it { expect(AccessToken.count).to eq 2 }
37
+ it { expect(AccessToken.last.client_id).to eq client.id }
38
+ it { expect(AccessToken.last.resource_owner_id).to eq user.id }
39
+ it { expect(AccessToken.last.scopes).to be_empty }
40
+
41
+ it { expect(json_body[:access_token]).to eq AccessToken.last.token }
42
+ it { expect(json_body[:token_type]).to eq 'bearer' }
43
+ it { expect(json_body[:expires_in]).to eq 7200 }
44
+ it { expect(json_body[:refresh_token]).to eq AccessToken.last.refresh_token }
45
+ end
46
+
47
+ context 'returns a new Access Token even if used token is expired' do
48
+ before do
49
+ allow(Simple::OAuth2.config).to receive(:issue_refresh_token).and_return(true)
50
+ access_token.update(expires_at: Time.now - 604_800) # - 7 days
51
+ subject.call
52
+ end
53
+
54
+ it { expect(access_token.refresh_token).not_to be_nil }
55
+ it { expect(last_response.status).to eq 200 }
56
+
57
+ it { expect(AccessToken.count).to eq 2 }
58
+ it { expect(AccessToken.last.client_id).to eq client.id }
59
+ it { expect(AccessToken.last.resource_owner_id).to eq user.id }
60
+ it { expect(AccessToken.last.scopes).to be_empty }
61
+
62
+ it { expect(json_body[:access_token]).to eq AccessToken.last.token }
63
+ it { expect(json_body[:token_type]).to eq 'bearer' }
64
+ it { expect(json_body[:expires_in]).to eq 7200 }
65
+ it { expect(json_body[:refresh_token]).to eq AccessToken.last.refresh_token }
66
+ end
67
+
68
+ context 'return a new Access Token with scopes' do
69
+ before do
70
+ allow(Simple::OAuth2.config).to receive(:issue_refresh_token).and_return(true)
71
+ subject.call
72
+ end
73
+
74
+ let(:scopes) { 'read' }
75
+
76
+ it { expect(AccessToken.count).to eq 2 }
77
+ it { expect(AccessToken.last.scopes).to eq 'read' }
78
+
79
+ it { expect(json_body[:scope]).to eq 'read' }
80
+ end
81
+
82
+ context 'revokes old Access Token if it is configured' do
83
+ before do
84
+ allow(Simple::OAuth2.config).to receive(:issue_refresh_token).and_return(true)
85
+ allow(Simple::OAuth2.config).to receive(:on_refresh).and_return(:revoke!)
86
+ subject.call
87
+ end
88
+
89
+ it { expect(last_response.status).to eq 200 }
90
+
91
+ it { expect(AccessToken.count).to eq 2 }
92
+ it { expect(AccessToken.last.client_id).to eq client.id }
93
+ it { expect(AccessToken.last.resource_owner_id).to eq user.id }
94
+
95
+ it { expect(access_token.reload.revoked?).to be_truthy }
96
+
97
+ it { expect(json_body[:access_token]).to eq AccessToken.last.token }
98
+ it { expect(json_body[:refresh_token]).to eq AccessToken.last.refresh_token }
99
+ end
100
+
101
+ context 'destroy old Access Token if it is configured' do
102
+ before do
103
+ allow(Simple::OAuth2.config).to receive(:issue_refresh_token).and_return(true)
104
+ allow(Simple::OAuth2.config).to receive(:on_refresh).and_return(:destroy)
105
+ subject.call
106
+ end
107
+
108
+ it { expect(last_response.status).to eq 200 }
109
+
110
+ it { expect(AccessToken.count).to eq 1 }
111
+ it { expect(AccessToken.where(token: access_token.token).first).to be_nil }
112
+
113
+ it { expect(json_body[:access_token]).to eq AccessToken.last.token }
114
+ it { expect(json_body[:token_type]).to eq 'bearer' }
115
+ it { expect(json_body[:expires_in]).to eq 7200 }
116
+ it { expect(json_body[:refresh_token]).to eq AccessToken.last.refresh_token }
117
+ end
118
+
119
+ context 'calls custom block on token refresh if it is configured' do
120
+ before do
121
+ allow(Simple::OAuth2.config).to receive(:issue_refresh_token).and_return(true)
122
+ allow(Simple::OAuth2.config).to receive(:on_refresh).and_return(
123
+ ->(token) { token.update(scopes: scopes) }
124
+ )
125
+ subject.call
126
+ end
127
+ let(:scopes) { 'for example' }
128
+
129
+ it { expect(last_response.status).to eq 200 }
130
+
131
+ it { expect(AccessToken.count).to eq 2 }
132
+ it { expect(access_token.reload.scopes).to eq(scopes) }
133
+ end
134
+
135
+ context 'does nothing on token refresh if :on_refresh is equal to' do
136
+ let(:callback) { nil }
137
+
138
+ before do
139
+ allow(Simple::OAuth2.config).to receive(:issue_refresh_token).and_return(true)
140
+ allow(Simple::OAuth2.config).to receive(:on_refresh).and_return(callback)
141
+ end
142
+
143
+ context 'nil' do
144
+ before { subject.call }
145
+
146
+ it { expect(Simple::OAuth2::Strategies::RefreshToken).not_to receive(:run_callback_on_refresh_token) }
147
+ it { expect(last_response.status).to eq 200 }
148
+ end
149
+
150
+ context ':nothing' do
151
+ let(:callback) { :nothing }
152
+ before { subject.call }
153
+
154
+ it { expect(Simple::OAuth2::Strategies::RefreshToken).not_to receive(:run_callback_on_refresh_token) }
155
+ it { expect(last_response.status).to eq 200 }
156
+ end
157
+
158
+ context 'String' do
159
+ let(:callback) { 'SomeClass' }
160
+ let(:error) do
161
+ ":on_refresh is not a block and Access Token class doesn't respond to #{callback}!"
162
+ end
163
+
164
+ it { expect { subject.call }.to raise_error(ArgumentError, error) }
165
+ end
166
+ end
167
+ end
168
+
169
+ context 'when invalid params' do
170
+ before do
171
+ allow(Simple::OAuth2.config).to receive(:issue_refresh_token).and_return(true)
172
+ subject.call
173
+ end
174
+
175
+ context 'without client_id' do
176
+ let(:client_id) {}
177
+
178
+ it { expect(AccessToken.count).to eq 1 }
179
+
180
+ it { expect(json_body[:error]).to eq('invalid_request') }
181
+ it { expect(json_body[:error_description]).to eq("'client_id' required.") }
182
+ it { expect(last_response.status).to eq 400 }
183
+ end
184
+
185
+ context 'with invalid client_id' do
186
+ let(:client_id) { 'invalid' }
187
+ let(:error_description) do
188
+ 'The client identifier provided is invalid, the client failed to authenticate, '\
189
+ 'the client did not include its credentials, provided multiple client credentials, '\
190
+ 'or used unsupported credentials type.'
191
+ end
192
+
193
+ it { expect(AccessToken.count).to eq 1 }
194
+
195
+ it { expect(json_body[:error]).to eq('invalid_client') }
196
+ it { expect(json_body[:error_description]).to eq(error_description) }
197
+ it { expect(last_response.status).to eq 401 }
198
+ end
199
+
200
+ context 'without client_secret' do
201
+ let(:client_secret) {}
202
+ let(:error_description) do
203
+ 'The client identifier provided is invalid, the client failed to authenticate, '\
204
+ 'the client did not include its credentials, provided multiple client credentials, '\
205
+ 'or used unsupported credentials type.'
206
+ end
207
+
208
+ it { expect(AccessToken.count).to eq 1 }
209
+
210
+ it { expect(json_body[:error]).to eq('invalid_client') }
211
+ it { expect(json_body[:error_description]).to eq(error_description) }
212
+ it { expect(last_response.status).to eq 401 }
213
+ end
214
+
215
+ context 'with invalid client_secret' do
216
+ let(:client_secret) { 'invalid' }
217
+ let(:error_description) do
218
+ 'The client identifier provided is invalid, the client failed to authenticate, '\
219
+ 'the client did not include its credentials, provided multiple client credentials, '\
220
+ 'or used unsupported credentials type.'
221
+ end
222
+
223
+ it { expect(AccessToken.count).to eq 1 }
224
+
225
+ it { expect(json_body[:error]).to eq('invalid_client') }
226
+ it { expect(json_body[:error_description]).to eq(error_description) }
227
+ it { expect(last_response.status).to eq 401 }
228
+ end
229
+
230
+ context 'without refresh_token' do
231
+ let(:refresh_token) {}
232
+
233
+ it { expect(AccessToken.count).to be_zero }
234
+
235
+ it { expect(json_body[:error]).to eq('invalid_request') }
236
+ it { expect(json_body[:error_description]).to eq("'refresh_token' required.") }
237
+ it { expect(last_response.status).to eq 400 }
238
+ end
239
+
240
+ context 'with invalid refresh_token' do
241
+ let(:refresh_token) { 'invalid' }
242
+ let(:error_description) do
243
+ 'The provided access grant is invalid, expired, or revoked '\
244
+ '(e.g. invalid assertion, expired authorization token, '\
245
+ 'bad end-user password credentials, or mismatching authorization code and redirection URI).'
246
+ end
247
+
248
+ it { expect(AccessToken.count).to be_zero }
249
+
250
+ it { expect(json_body[:error]).to eq('invalid_grant') }
251
+ it { expect(json_body[:error_description]).to eq(error_description) }
252
+ it { expect(last_response.status).to eq 400 }
253
+ end
254
+
255
+ context 'without grant_type' do
256
+ let(:grant_type) {}
257
+
258
+ it { expect(AccessToken.count).to eq 1 }
259
+
260
+ it { expect(json_body[:error]).to eq('invalid_request') }
261
+ it { expect(json_body[:error_description]).to eq("'grant_type' required.") }
262
+ it { expect(last_response.status).to eq 400 }
263
+ end
264
+
265
+ context 'with invalid grant_type' do
266
+ let(:grant_type) { 'invalid' }
267
+ let(:error_description) do
268
+ 'The access grant included - '\
269
+ 'its type or another attribute - '\
270
+ 'is not supported by the authorization server.'
271
+ end
272
+
273
+ it { expect(AccessToken.count).to eq 1 }
274
+
275
+ it { expect(json_body[:error]).to eq('unsupported_grant_type') }
276
+ it { expect(json_body[:error_description]).to eq(error_description) }
277
+ it { expect(last_response.status).to eq 400 }
278
+ end
279
+ end
280
+ end
281
+ end
282
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Authorization Endpoint' do
4
+ subject { -> { post url, params } }
5
+
6
+ let(:url) { '/oauth/authorization' }
7
+ let!(:user) { User.create(username: FFaker::Internet.user_name, encrypted_password: FFaker::Internet.password) }
8
+ let(:client) { Client.create(name: FFaker::Internet.domain_word, redirect_uri: 'http://localhost:3000/home') }
9
+ let(:client_id) { client.key }
10
+ let(:redirect_uri) { client.redirect_uri }
11
+ let(:response_type) { 'token' }
12
+ let(:state) { nil }
13
+ let(:scope) { nil }
14
+ let(:params) do
15
+ {
16
+ client_id: client_id,
17
+ redirect_uri: redirect_uri,
18
+ response_type: response_type,
19
+ state: state,
20
+ scope: scope
21
+ }
22
+ end
23
+
24
+ describe 'POST /oauth/custom_authorization' do
25
+ let(:url) { '/oauth/custom_authorization' }
26
+
27
+ before { subject.call }
28
+
29
+ context 'invokes custom block' do
30
+ it { expect(last_response.status).to eq(400) }
31
+ end
32
+ end
33
+
34
+ describe 'POST /oauth/authorization' do
35
+ describe 'Token flow' do
36
+ before { subject.call }
37
+
38
+ context 'when valid params' do
39
+ let(:access_token) { AccessToken.last.token }
40
+ let(:response_header) do
41
+ "#{client.redirect_uri}#access_token=#{access_token}&expires_in=7200&token_type=bearer"
42
+ end
43
+
44
+ it { expect(AccessToken.count).to eq 1 }
45
+ it { expect(AccessToken.last.scopes).to be_empty }
46
+ it { expect(AccessToken.last.resource_owner.username).to eq user.username }
47
+ it { expect(last_response.status).to eq 302 }
48
+ it { expect(last_response.headers['Location']).to eq response_header }
49
+
50
+ context 'without redirect_uri' do
51
+ let(:redirect_uri) {}
52
+
53
+ it { expect(last_response.headers['Location']).to eq response_header }
54
+ end
55
+
56
+ context 'with state' do
57
+ let(:state) { 'zxc' }
58
+ let(:response_header_with_state) do
59
+ "#{client.redirect_uri}#access_token=#{access_token}&expires_in=7200&state=#{state}&token_type=bearer"
60
+ end
61
+
62
+ it { expect(last_response.headers['Location']).to eq response_header_with_state }
63
+ end
64
+
65
+ context 'with scopes' do
66
+ let(:scope) { 'read,write' }
67
+
68
+ it { expect(AccessToken.last.scopes).to eq 'read,write' }
69
+ end
70
+ end
71
+
72
+ context 'when invalid params' do
73
+ context 'without client_id' do
74
+ let(:client_id) {}
75
+
76
+ it { expect(last_response.status).to eq 400 }
77
+ it { expect(json_body[:error]).to eq('bad_request') }
78
+ end
79
+
80
+ context 'with invalid client_id' do
81
+ let(:client_id) { 'invalid' }
82
+
83
+ it { expect(last_response.status).to eq 400 }
84
+ it { expect(json_body[:error]).to eq('bad_request') }
85
+ end
86
+
87
+ context 'with invalid redirect_uri' do
88
+ let(:redirect_uri) { 'invalid' }
89
+
90
+ it { expect(last_response.status).to eq 400 }
91
+ it { expect(json_body[:error]).to eq('bad_request') }
92
+ end
93
+
94
+ context 'without response_type' do
95
+ let(:response_type) {}
96
+
97
+ it { expect(last_response.status).to eq 400 }
98
+ it { expect(json_body[:error]).to eq('invalid_request') }
99
+ it { expect(json_body[:error_description]).to eq "'response_type' required." }
100
+ end
101
+
102
+ context 'with invalid response_type' do
103
+ let(:response_type) { 'invalid' }
104
+ let(:error_description) { 'The requested response type is not supported by the authorization server.' }
105
+
106
+ it { expect(last_response.status).to eq 400 }
107
+ it { expect(json_body[:error]).to eq 'unsupported_response_type' }
108
+ it { expect(json_body[:error_description]).to eq error_description }
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'GET Protected Resources' do
4
+ subject { -> { get url, params } }
5
+
6
+ let(:url) { '/api/v1/status' }
7
+ let(:client) { Client.create(name: FFaker::Internet.domain_word, redirect_uri: 'http://localhost:3000/home') }
8
+ let(:user) { User.create(username: FFaker::Internet.user_name, encrypted_password: FFaker::Internet.password) }
9
+ let(:scopes) { nil }
10
+ let(:access_token) { AccessToken.create_for(client, user, scopes) }
11
+ let(:params) { { access_token: access_token.token } }
12
+
13
+ before { subject.call }
14
+
15
+ context 'with invalid data' do
16
+ context 'returns Unauthorized without access_token' do
17
+ let(:params) {}
18
+
19
+ it { expect(last_response.status).to eq 401 }
20
+ it { expect(json_body[:error]).to eq 'unauthorized' }
21
+ it { expect(last_response.headers['WWW-Authenticate']).to eq('Bearer realm="Custom Realm"') }
22
+ end
23
+
24
+ context 'returns Unauthorized when token scopes are blank' do
25
+ let(:url) { '/api/v1/status/single_scope' }
26
+
27
+ it { expect(last_response.status).to eq 403 }
28
+ it { expect(json_body[:error]).to eq 'forbidden' }
29
+ end
30
+
31
+ context "returns Unauthorized when token scopes doesn't match required scopes" do
32
+ let(:url) { '/api/v1/status/multiple_scopes' }
33
+ let(:scopes) { 'read' }
34
+
35
+ it { expect(last_response.status).to eq 403 }
36
+ it { expect(json_body[:error]).to eq 'forbidden' }
37
+ end
38
+ end
39
+
40
+ context 'with valid data' do
41
+ context "returns status for endpoint that doesn't requires any scope" do
42
+ it { expect(last_response.status).to eq 200 }
43
+ it { expect(json_body[:value]).to eq('Access') }
44
+ it { expect(json_body[:current_user_name]).not_to be_nil }
45
+ end
46
+
47
+ context 'returns status for endpoint with specific scope' do
48
+ let(:url) { '/api/v1/status/single_scope' }
49
+ let(:scopes) { 'read' }
50
+
51
+ it { expect(last_response.status).to eq 200 }
52
+ it { expect(json_body[:value]).to eq('Access read') }
53
+ it { expect(json_body[:current_user_name]).not_to be_nil }
54
+ end
55
+
56
+ context 'returns status for endpoint with specific set of scopes' do
57
+ let(:url) { '/api/v1/status/multiple_scopes' }
58
+ let(:scopes) { 'read,write' }
59
+
60
+ it { expect(last_response.status).to eq 200 }
61
+ it { expect(json_body[:value]).to eq('Access read, write') }
62
+ it { expect(json_body[:current_user_name]).not_to be_nil }
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'RevokeToken Endpoint' do
4
+ subject { -> { post url, params } }
5
+
6
+ let(:url) { '/oauth/revoke_token' }
7
+ let(:user) { User.create(username: FFaker::Internet.user_name, encrypted_password: FFaker::Internet.password) }
8
+ let(:client) { Client.create(name: FFaker::Internet.domain_word, redirect_uri: 'http://localhost:3000/home') }
9
+ let(:access_token) { AccessToken.create_for(client, user) }
10
+ let(:client_id) { client.key }
11
+ let(:refresh_token) { access_token.refresh_token }
12
+ let(:params) do
13
+ {
14
+ client_id: client_id,
15
+ token: refresh_token
16
+ }
17
+ end
18
+
19
+ describe 'POST /oauth/revoke_token' do
20
+ before do
21
+ allow(Simple::OAuth2.config).to receive(:issue_refresh_token).and_return(true)
22
+ subject.call
23
+ end
24
+
25
+ context 'with valid params' do
26
+ it { expect(last_response.status).to eq 200 }
27
+ it { expect(last_response.header).to be_empty }
28
+ it { expect(last_response.body).to be_empty }
29
+
30
+ it { expect(access_token.reload.refresh_token).to_not be_nil }
31
+ it { expect(access_token.reload.revoked_at).to_not be_nil }
32
+ end
33
+
34
+ context 'with invalid params' do
35
+ context 'without client_id' do
36
+ let(:client_id) {}
37
+ let(:error_description) do
38
+ 'The client identifier provided is invalid, the client failed to authenticate, '\
39
+ 'the client did not include its credentials, provided multiple client credentials, '\
40
+ 'or used unsupported credentials type.'
41
+ end
42
+
43
+ it { expect(last_response.status).to eq 401 }
44
+ it { expect(json_body[:error]).to eq 'invalid_client' }
45
+ it { expect(json_body[:error_description]).to eq error_description }
46
+
47
+ it { expect(access_token.reload.refresh_token).to_not be_nil }
48
+ it { expect(access_token.reload.revoked_at).to be_nil }
49
+ end
50
+
51
+ context 'when client_id invalid' do
52
+ let(:client_id) { 'invalid' }
53
+ let(:error_description) do
54
+ 'The client identifier provided is invalid, the client failed to authenticate, '\
55
+ 'the client did not include its credentials, provided multiple client credentials, '\
56
+ 'or used unsupported credentials type.'
57
+ end
58
+
59
+ it { expect(last_response.status).to eq 401 }
60
+ it { expect(json_body[:error]).to eq 'invalid_client' }
61
+ it { expect(json_body[:error_description]).to eq error_description }
62
+
63
+ it { expect(access_token.reload.refresh_token).to_not be_nil }
64
+ it { expect(access_token.reload.revoked_at).to be_nil }
65
+ end
66
+
67
+ context 'without token' do
68
+ let(:refresh_token) {}
69
+
70
+ it { expect(last_response.status).to eq 200 }
71
+ it { expect(last_response.header).to be_empty }
72
+ it { expect(last_response.body).to be_empty }
73
+
74
+ it { expect(access_token.reload.refresh_token).to_not be_nil }
75
+ it { expect(access_token.reload.revoked_at).to be_nil }
76
+ end
77
+
78
+ context 'when token invalid' do
79
+ let(:refresh_token) { 'invalid' }
80
+
81
+ it { expect(last_response.status).to eq 200 }
82
+ it { expect(last_response.header).to be_empty }
83
+ it { expect(last_response.body).to be_empty }
84
+
85
+ it { expect(access_token.reload.refresh_token).to_not be_nil }
86
+ it { expect(access_token.reload.revoked_at).to be_nil }
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,51 @@
1
+ ENV['RAILS_ENV'] ||= 'test'
2
+ ENV['ORM'] ||= 'nobrainer'
3
+
4
+ ORM_GEMS_MAPPING = {
5
+ 'nobrainer' => 'nobrainer'
6
+ }.freeze
7
+
8
+ if RUBY_VERSION >= '1.9'
9
+ require 'simplecov'
10
+ require 'coveralls'
11
+
12
+ SimpleCov.formatters = [
13
+ SimpleCov::Formatter::HTMLFormatter,
14
+ Coveralls::SimpleCov::Formatter
15
+ ]
16
+
17
+ SimpleCov.start do
18
+ add_filter '/spec/'
19
+ minimum_coverage(90)
20
+ end
21
+ end
22
+
23
+ require 'rack/test'
24
+ require 'ffaker'
25
+ require ORM_GEMS_MAPPING[ENV['ORM']]
26
+ require File.expand_path("../dummy/orm/#{ENV['ORM']}/app/twitter", __FILE__)
27
+
28
+ APP = Rack::Builder.parse_file(File.expand_path("../dummy/orm/#{ENV['ORM']}/config.ru", __FILE__)).first
29
+
30
+ require 'support/helper'
31
+
32
+ RSpec.configure do |config|
33
+ config.include Helper
34
+
35
+ config.filter_run_excluding skip_if: true
36
+ config.expect_with :rspec do |c|
37
+ c.syntax = :expect
38
+ end
39
+
40
+ config.order = :random
41
+ config.color = true
42
+
43
+ config.before(:all) do
44
+ NoBrainer.sync_schema
45
+ end
46
+
47
+ config.before(:each) do
48
+ NoBrainer.purge!
49
+ NoBrainer::Loader.cleanup
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ module Helper
2
+ include Rack::Test::Methods
3
+
4
+ def app
5
+ APP
6
+ end
7
+
8
+ def json_body
9
+ JSON.parse(last_response.body, symbolize_names: true)
10
+ end
11
+ end