simple_oauth2 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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