grape_oauth2 0.1.1

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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +18 -0
  5. data/.travis.yml +42 -0
  6. data/Gemfile +23 -0
  7. data/README.md +820 -0
  8. data/Rakefile +11 -0
  9. data/gemfiles/active_record.rb +25 -0
  10. data/gemfiles/mongoid.rb +14 -0
  11. data/gemfiles/sequel.rb +24 -0
  12. data/grape_oauth2.gemspec +27 -0
  13. data/grape_oauth2.png +0 -0
  14. data/lib/grape_oauth2.rb +129 -0
  15. data/lib/grape_oauth2/configuration.rb +143 -0
  16. data/lib/grape_oauth2/configuration/class_accessors.rb +36 -0
  17. data/lib/grape_oauth2/configuration/validation.rb +71 -0
  18. data/lib/grape_oauth2/endpoints/authorize.rb +34 -0
  19. data/lib/grape_oauth2/endpoints/token.rb +72 -0
  20. data/lib/grape_oauth2/gem_version.rb +24 -0
  21. data/lib/grape_oauth2/generators/authorization.rb +44 -0
  22. data/lib/grape_oauth2/generators/base.rb +26 -0
  23. data/lib/grape_oauth2/generators/token.rb +62 -0
  24. data/lib/grape_oauth2/helpers/access_token_helpers.rb +54 -0
  25. data/lib/grape_oauth2/helpers/oauth_params.rb +41 -0
  26. data/lib/grape_oauth2/mixins/active_record/access_grant.rb +47 -0
  27. data/lib/grape_oauth2/mixins/active_record/access_token.rb +75 -0
  28. data/lib/grape_oauth2/mixins/active_record/client.rb +35 -0
  29. data/lib/grape_oauth2/mixins/mongoid/access_grant.rb +58 -0
  30. data/lib/grape_oauth2/mixins/mongoid/access_token.rb +88 -0
  31. data/lib/grape_oauth2/mixins/mongoid/client.rb +41 -0
  32. data/lib/grape_oauth2/mixins/sequel/access_grant.rb +68 -0
  33. data/lib/grape_oauth2/mixins/sequel/access_token.rb +86 -0
  34. data/lib/grape_oauth2/mixins/sequel/client.rb +46 -0
  35. data/lib/grape_oauth2/responses/authorization.rb +10 -0
  36. data/lib/grape_oauth2/responses/base.rb +56 -0
  37. data/lib/grape_oauth2/responses/token.rb +10 -0
  38. data/lib/grape_oauth2/scopes.rb +74 -0
  39. data/lib/grape_oauth2/strategies/authorization_code.rb +38 -0
  40. data/lib/grape_oauth2/strategies/base.rb +47 -0
  41. data/lib/grape_oauth2/strategies/client_credentials.rb +20 -0
  42. data/lib/grape_oauth2/strategies/password.rb +22 -0
  43. data/lib/grape_oauth2/strategies/refresh_token.rb +47 -0
  44. data/lib/grape_oauth2/unique_token.rb +20 -0
  45. data/lib/grape_oauth2/version.rb +14 -0
  46. data/spec/configuration/config_spec.rb +231 -0
  47. data/spec/configuration/version_spec.rb +12 -0
  48. data/spec/dummy/endpoints/custom_authorization.rb +25 -0
  49. data/spec/dummy/endpoints/custom_token.rb +35 -0
  50. data/spec/dummy/endpoints/status.rb +25 -0
  51. data/spec/dummy/grape_oauth2_config.rb +11 -0
  52. data/spec/dummy/orm/active_record/app/config/db.rb +7 -0
  53. data/spec/dummy/orm/active_record/app/models/access_code.rb +3 -0
  54. data/spec/dummy/orm/active_record/app/models/access_token.rb +3 -0
  55. data/spec/dummy/orm/active_record/app/models/application.rb +3 -0
  56. data/spec/dummy/orm/active_record/app/models/application_record.rb +3 -0
  57. data/spec/dummy/orm/active_record/app/models/user.rb +10 -0
  58. data/spec/dummy/orm/active_record/app/twitter.rb +36 -0
  59. data/spec/dummy/orm/active_record/config.ru +7 -0
  60. data/spec/dummy/orm/active_record/db/schema.rb +53 -0
  61. data/spec/dummy/orm/mongoid/app/config/db.rb +6 -0
  62. data/spec/dummy/orm/mongoid/app/config/mongoid.yml +21 -0
  63. data/spec/dummy/orm/mongoid/app/models/access_code.rb +3 -0
  64. data/spec/dummy/orm/mongoid/app/models/access_token.rb +3 -0
  65. data/spec/dummy/orm/mongoid/app/models/application.rb +3 -0
  66. data/spec/dummy/orm/mongoid/app/models/user.rb +11 -0
  67. data/spec/dummy/orm/mongoid/app/twitter.rb +34 -0
  68. data/spec/dummy/orm/mongoid/config.ru +5 -0
  69. data/spec/dummy/orm/sequel/app/config/db.rb +1 -0
  70. data/spec/dummy/orm/sequel/app/models/access_code.rb +4 -0
  71. data/spec/dummy/orm/sequel/app/models/access_token.rb +4 -0
  72. data/spec/dummy/orm/sequel/app/models/application.rb +4 -0
  73. data/spec/dummy/orm/sequel/app/models/application_record.rb +2 -0
  74. data/spec/dummy/orm/sequel/app/models/user.rb +11 -0
  75. data/spec/dummy/orm/sequel/app/twitter.rb +47 -0
  76. data/spec/dummy/orm/sequel/config.ru +5 -0
  77. data/spec/dummy/orm/sequel/db/schema.rb +50 -0
  78. data/spec/lib/scopes_spec.rb +50 -0
  79. data/spec/mixins/active_record/access_token_spec.rb +185 -0
  80. data/spec/mixins/active_record/client_spec.rb +95 -0
  81. data/spec/mixins/mongoid/access_token_spec.rb +185 -0
  82. data/spec/mixins/mongoid/client_spec.rb +95 -0
  83. data/spec/mixins/sequel/access_token_spec.rb +185 -0
  84. data/spec/mixins/sequel/client_spec.rb +96 -0
  85. data/spec/requests/flows/authorization_code_spec.rb +67 -0
  86. data/spec/requests/flows/client_credentials_spec.rb +101 -0
  87. data/spec/requests/flows/password_spec.rb +210 -0
  88. data/spec/requests/flows/refresh_token_spec.rb +222 -0
  89. data/spec/requests/flows/revoke_token_spec.rb +103 -0
  90. data/spec/requests/protected_resources_spec.rb +64 -0
  91. data/spec/spec_helper.rb +60 -0
  92. data/spec/support/api_helper.rb +11 -0
  93. metadata +257 -0
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Grape::OAuth2::Mongoid::Client', skip_if: ENV['ORM'] != 'mongoid' do
4
+ let(:client) { Application.new }
5
+
6
+ let(:key) { SecureRandom.hex(8) }
7
+ let(:secret) { SecureRandom.hex(8) }
8
+
9
+ it 'generates key on create' do
10
+ expect(client.key).to be_nil
11
+ client.save
12
+ expect(client.key).not_to be_nil
13
+ end
14
+
15
+ it 'generates key on create if an empty string' do
16
+ client.key = ''
17
+ client.save
18
+ expect(client.key).not_to be_blank
19
+ end
20
+
21
+ it 'generates key on create unless one is set' do
22
+ client.key = key
23
+ client.save
24
+ expect(client.key).to eq(key)
25
+ end
26
+
27
+ it 'is invalid without key' do
28
+ client.save
29
+ client.key = nil
30
+ expect(client).not_to be_valid
31
+ end
32
+
33
+ it 'checks uniqueness of key' do
34
+ app1 = Application.create
35
+ app2 = Application.create
36
+ app2.key = app1.key
37
+ expect(app2).not_to be_valid
38
+ end
39
+
40
+ it 'expects database to throw an error when keys are the same' do
41
+ app1 = Application.create
42
+ app2 = Application.create
43
+ app2.key = app1.key
44
+ expect { app2.save! }.to raise_error(Mongoid::Errors::Validations)
45
+ end
46
+
47
+ it 'generate secret on create' do
48
+ expect(client.secret).to be_nil
49
+ client.save
50
+ expect(client.secret).not_to be_nil
51
+ end
52
+
53
+ it 'generate secret on create if is blank string' do
54
+ client.secret = ''
55
+ client.save
56
+ expect(client.secret).not_to be_blank
57
+ end
58
+
59
+ it 'generate secret on create unless one is set' do
60
+ client.secret = secret
61
+ client.save
62
+ expect(client.secret).to eq(secret)
63
+ end
64
+
65
+ it 'is invalid without secret' do
66
+ client.save
67
+ client.secret = nil
68
+ expect(client).not_to be_valid
69
+ end
70
+
71
+ describe '#authenticate' do
72
+ it 'returns a class instance if authenticated successfully' do
73
+ client.key = key
74
+ client.secret = secret
75
+ client.save
76
+
77
+ expect(Application.authenticate(key, secret)).to eq(client)
78
+ end
79
+
80
+ it 'returns a class instance if only key specified' do
81
+ client.key = key
82
+ client.save
83
+
84
+ expect(Application.authenticate(key)).to eq(client)
85
+ end
86
+
87
+ it 'returns nil if authentication failed' do
88
+ client.key = key
89
+ client.secret = secret
90
+ client.save
91
+
92
+ expect(Application.authenticate(key, 'invalid-')).to be_nil
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,185 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Grape::OAuth2::Sequel::AccessToken', skip_if: ENV['ORM'] != 'sequel' do
4
+ let(:application) { Application.create(name: 'Test') }
5
+ let(:user) { User.create(username: 'test', password: '123123') }
6
+ let(:access_token) { AccessToken.create(client: application, resource_owner: user) }
7
+
8
+ let(:token) { SecureRandom.hex(16) }
9
+
10
+ describe 'validations' do
11
+ it 'validate token uniqueness' do
12
+ another_token = AccessToken.create(client: application)
13
+ token = AccessToken.new(client: application, token: another_token.token)
14
+
15
+ expect(token).not_to be_valid
16
+ expect(token.errors).to include(:token)
17
+ end
18
+ end
19
+
20
+ describe '#to_bearer_token' do
21
+ context 'config with refresh token' do
22
+ before do
23
+ Grape::OAuth2.config.issue_refresh_token = true
24
+ end
25
+
26
+ after do
27
+ Grape::OAuth2.config.issue_refresh_token = false
28
+ end
29
+
30
+ it 'returns refresh token' do
31
+ expect(access_token.to_bearer_token[:access_token]).not_to be_blank
32
+ end
33
+ end
34
+
35
+ context 'config without refresh token' do
36
+ before do
37
+ Grape::OAuth2.configure do |config|
38
+ config.issue_refresh_token = false
39
+ end
40
+ end
41
+
42
+ it 'returns blank refresh token' do
43
+ expect(access_token.to_bearer_token[:refresh_token]).to be_blank
44
+ end
45
+ end
46
+ end
47
+
48
+ describe '#authenticate' do
49
+ it 'returns an instance if authenticated successfully' do
50
+ access_token.token = token
51
+ access_token.save
52
+
53
+ expect(AccessToken.authenticate(token)).to eq(access_token)
54
+ end
55
+
56
+ it 'returns nil if authentication failed' do
57
+ access_token.token = token
58
+ access_token.save
59
+
60
+ expect(AccessToken.authenticate("invalid-#{token}")).to be_nil
61
+ end
62
+
63
+ it 'returns an instance by refresh token' do
64
+ refresh_token = SecureRandom.hex(6)
65
+ token = AccessToken.create(client: application, refresh_token: refresh_token)
66
+
67
+ expect(AccessToken.authenticate(refresh_token, type: :refresh_token)).to eq(token)
68
+ expect(AccessToken.authenticate(refresh_token, type: 'refresh_token')).to eq(token)
69
+ end
70
+ end
71
+
72
+ describe '#create_for?' do
73
+ it 'creates a record only for Client' do
74
+ token = AccessToken.create_for(application, nil)
75
+
76
+ expect(token.client).not_to be_nil
77
+ expect(token.resource_owner).to be_nil
78
+ end
79
+
80
+ it 'creates a record for Client and Resource Owner' do
81
+ token = AccessToken.create_for(application, user)
82
+
83
+ expect(token.client).to eq(application)
84
+ expect(token.resource_owner).to eq(user)
85
+ end
86
+
87
+ it 'creates a record with scopes' do
88
+ scopes = 'write read'
89
+ token = AccessToken.create_for(application, user, scopes)
90
+
91
+ expect(token.client).to eq(application)
92
+ expect(token.resource_owner).to eq(user)
93
+ expect(token.scopes).to eq(scopes)
94
+ end
95
+ end
96
+
97
+ describe '#expired?' do
98
+ it 'return false if expires_at nil' do
99
+ access_token.update_fields({ expires_at: nil }, [:expires_at])
100
+
101
+ expect(access_token.expired?).to be_falsey
102
+ end
103
+
104
+ it 'return false if expires_at < Time.now' do
105
+ expect(access_token.expired?).to be_falsey
106
+ end
107
+
108
+ it 'return false if expires_at > Time.now' do
109
+ expires_at = Time.now.utc - Grape::OAuth2.config.access_token_lifetime + 1
110
+ access_token.update_fields({ expires_at: expires_at }, [:expires_at])
111
+
112
+ expect(access_token.expired?).to be_truthy
113
+ end
114
+ end
115
+
116
+ describe '#revoked?' do
117
+ it 'return false if revoked_at nil' do
118
+ access_token.update_fields({ revoked_at: nil }, [:revoked_at])
119
+
120
+ expect(access_token.revoked?).to be_falsey
121
+ end
122
+
123
+ it 'return false if revoked_at present' do
124
+ access_token.update_fields({ revoked_at: Time.now.utc }, [:revoked_at])
125
+ expect(access_token.revoked?).to be_truthy
126
+ end
127
+ end
128
+
129
+ describe '#revoke!' do
130
+ it 'update :revoked_at attribute' do
131
+ expect { access_token.revoke! }.to change { access_token.revoked? }.from(false).to(true)
132
+ end
133
+
134
+ it 'update :revoked_at attribute with custom value' do
135
+ custom_time = Time.now - 7200
136
+ access_token.revoke!(custom_time)
137
+
138
+ expect(access_token.revoked_at).to eq(custom_time.utc)
139
+ end
140
+ end
141
+
142
+ describe 'token generation' do
143
+ it 'generates a new token before saving if token is blank' do
144
+ token = AccessToken.new(client: application, resource_owner: user)
145
+
146
+ expect(token.token).to be_blank
147
+
148
+ token.save
149
+
150
+ expect(token.token).not_to be_blank
151
+ end
152
+
153
+ it 'does not change token value on saving if token is present' do
154
+ token = AccessToken.new(client: application, resource_owner: user, token: 'abcdef')
155
+
156
+ expect(token.token).not_to be_blank
157
+
158
+ token.save
159
+
160
+ expect(token.token).to eq('abcdef')
161
+ end
162
+ end
163
+
164
+ describe 'expiration' do
165
+ it 'set to nil if configuration option set to nil' do
166
+ Grape::OAuth2.config.access_token_lifetime = nil
167
+
168
+ token = AccessToken.create(client: application, resource_owner: user)
169
+ expect(token.expires_at).to be_nil
170
+
171
+ Grape::OAuth2.config.access_token_lifetime = Grape::OAuth2::Configuration::DEFAULT_TOKEN_LIFETIME
172
+ end
173
+
174
+ it 'set to specific time if configuration option set to some value' do
175
+ current_time = Time.now.utc
176
+ Grape::OAuth2.config.access_token_lifetime = 3500
177
+
178
+ token = AccessToken.create(client: application, resource_owner: user)
179
+ expect(token.expires_at).not_to be_nil
180
+ expect(token.expires_at).to be_within(1).of(current_time + 3500)
181
+
182
+ Grape::OAuth2.config.access_token_lifetime = Grape::OAuth2::Configuration::DEFAULT_TOKEN_LIFETIME
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Grape::OAuth2::Sequel::Client', skip_if: ENV['ORM'] != 'sequel' do
4
+ let(:client) { Application.new(name: 'Test') }
5
+
6
+ let(:key) { SecureRandom.hex(8) }
7
+ let(:secret) { SecureRandom.hex(8) }
8
+
9
+ it 'generates key on create' do
10
+ expect(client.key).to be_nil
11
+ client.save
12
+ expect(client.key).not_to be_nil
13
+ end
14
+
15
+ it 'generates key on create if an empty string' do
16
+ client.key = ''
17
+ client.save
18
+ expect(client.key).not_to be_blank
19
+ end
20
+
21
+ it 'generates key on create unless one is set' do
22
+ client.key = key
23
+ client.save
24
+ expect(client.key).to eq(key)
25
+ end
26
+
27
+ it 'is invalid without key' do
28
+ client.save
29
+ client.key = nil
30
+ expect(client).not_to be_valid
31
+ end
32
+
33
+ it 'checks uniqueness of key' do
34
+ app1 = Application.create(name: 'app1')
35
+ app2 = Application.create(name: 'app2')
36
+ app2.key = app1.key
37
+ expect(app2).not_to be_valid
38
+ expect(app2.errors).to include(:key)
39
+ end
40
+
41
+ it 'expects database to throw an error when keys are the same' do
42
+ app1 = Application.create(name: 'app1')
43
+ app2 = Application.create(name: 'app2')
44
+ app2.key = app1.key
45
+ expect { app2.save }.to raise_error(Sequel::ValidationFailed)
46
+ end
47
+
48
+ it 'generate secret on create' do
49
+ expect(client.secret).to be_nil
50
+ client.save
51
+ expect(client.secret).not_to be_nil
52
+ end
53
+
54
+ it 'generate secret on create if is blank string' do
55
+ client.secret = ''
56
+ client.save
57
+ expect(client.secret).not_to be_blank
58
+ end
59
+
60
+ it 'generate secret on create unless one is set' do
61
+ client.secret = secret
62
+ client.save
63
+ expect(client.secret).to eq(secret)
64
+ end
65
+
66
+ it 'is invalid without secret' do
67
+ client.save
68
+ client.secret = nil
69
+ expect(client).not_to be_valid
70
+ end
71
+
72
+ describe '#authenticate' do
73
+ it 'returns a class instance if authenticated successfully' do
74
+ client.key = key
75
+ client.secret = secret
76
+ client.save
77
+
78
+ expect(Application.authenticate(key, secret)).to eq(client)
79
+ end
80
+
81
+ it 'returns a class instance if only key specified' do
82
+ client.key = key
83
+ client.save
84
+
85
+ expect(Application.authenticate(key)).to eq(client)
86
+ end
87
+
88
+ it 'returns nil if authentication failed' do
89
+ client.key = key
90
+ client.secret = secret
91
+ client.save
92
+
93
+ expect(Application.authenticate(key, 'invalid-')).to be_nil
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Authorization Code flow' do
4
+ let(:redirect_uri) { 'http://localhost:3000/home' }
5
+ let(:application) { Application.create(name: 'App1', redirect_uri: redirect_uri) }
6
+
7
+ describe 'POST /oauth/authorize' do
8
+ let(:authorize_url) { '/api/v1/oauth/authorize' }
9
+
10
+ context 'with valid params' do
11
+ context 'when response_type is :code' do
12
+ it 'should be success' do
13
+ expect {
14
+ post authorize_url,
15
+ client_id: application.key,
16
+ redirect_uri: redirect_uri,
17
+ response_type: 'code'
18
+ }.to change { AccessCode.count }.from(0).to(1)
19
+
20
+ expect(last_response.status).to eq 302
21
+ end
22
+ end
23
+
24
+ context 'when response_type is :token' do
25
+ it 'should be success' do
26
+ expect {
27
+ post authorize_url,
28
+ client_id: application.key,
29
+ redirect_uri: redirect_uri,
30
+ response_type: 'token'
31
+ }.to change { AccessToken.count }.from(0).to(1)
32
+ end
33
+ end
34
+ end
35
+
36
+ context 'with invalid params' do
37
+ it 'should fail without response_type' do
38
+ post authorize_url,
39
+ client_id: application.key
40
+
41
+ expect(last_response.status).to eq 400
42
+ expect(json_body[:error]).to eq('invalid_request')
43
+ end
44
+
45
+ it 'should fail with unsupported response_type' do
46
+ post authorize_url,
47
+ client_id: application.key,
48
+ redirect_uri: redirect_uri,
49
+ response_type: 'invalid'
50
+
51
+ expect(last_response.status).to eq 400
52
+ expect(json_body[:error]).to eq('unsupported_response_type')
53
+ end
54
+ end
55
+ end
56
+
57
+ describe 'POST /oauth/custom_authorize' do
58
+ it 'invokes custom block' do
59
+ post '/api/v1/oauth/custom_authorize',
60
+ client_id: application.key,
61
+ redirect_uri: redirect_uri,
62
+ response_type: 'code'
63
+
64
+ expect(last_response.status).to eq(400)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Token Endpoint' do
4
+ describe 'POST /oauth/token' do
5
+ describe 'Client Credentials flow' do
6
+ context 'with valid params' do
7
+ let(:authentication_url) { '/api/v1/oauth/token' }
8
+ let(:application) { Application.create(name: 'App1') }
9
+ let(:user) { User.create(username: 'test', password: '12345678') }
10
+
11
+ context 'when request is invalid' do
12
+ it 'fails without Grant Type' do
13
+ post authentication_url,
14
+ client_id: application.key,
15
+ client_secret: application.secret
16
+
17
+ expect(AccessToken.all).to be_empty
18
+
19
+ expect(json_body[:error]).to eq('invalid_request')
20
+ expect(last_response.status).to eq 400
21
+ end
22
+
23
+ it 'fails with invalid Grant Type' do
24
+ post authentication_url,
25
+ grant_type: 'invalid'
26
+
27
+ expect(AccessToken.all).to be_empty
28
+
29
+ expect(json_body[:error]).to eq('unsupported_grant_type')
30
+ expect(last_response.status).to eq 400
31
+ end
32
+
33
+ it 'fails without Client Credentials' do
34
+ post authentication_url,
35
+ grant_type: 'client_credentials'
36
+
37
+ expect(AccessToken.all).to be_empty
38
+
39
+ expect(json_body[:error]).to eq('invalid_request')
40
+ expect(last_response.status).to eq 400
41
+ end
42
+
43
+ it 'fails with invalid Client Credentials' do
44
+ post authentication_url,
45
+ grant_type: 'client_credentials',
46
+ client_id: 'blah-blah',
47
+ client_secret: application.secret
48
+
49
+ expect(AccessToken.all).to be_empty
50
+
51
+ expect(json_body[:error]).to eq('invalid_client')
52
+ expect(last_response.status).to eq 401
53
+ end
54
+ end
55
+
56
+ context 'with valid data' do
57
+ context 'when scopes requested' do
58
+ it 'returns an Access Token with scopes' do
59
+ post authentication_url,
60
+ grant_type: 'client_credentials',
61
+ scope: 'read write',
62
+ client_id: application.key,
63
+ client_secret: application.secret
64
+
65
+ expect(AccessToken.count).to eq 1
66
+ expect(AccessToken.first.client_id).to eq application.id
67
+
68
+ expect(json_body[:access_token]).to be_present
69
+ expect(json_body[:token_type]).to eq 'bearer'
70
+ expect(json_body[:expires_in]).to eq 7200
71
+ expect(json_body[:refresh_token]).to be_nil
72
+ expect(json_body[:scope]).to eq('read write')
73
+
74
+ expect(last_response.status).to eq 200
75
+ end
76
+ end
77
+
78
+ context 'without scopes' do
79
+ it 'returns an Access Token without scopes' do
80
+ post authentication_url,
81
+ grant_type: 'client_credentials',
82
+ client_id: application.key,
83
+ client_secret: application.secret
84
+
85
+ expect(AccessToken.count).to eq 1
86
+ expect(AccessToken.first.client_id).to eq application.id
87
+
88
+ expect(json_body[:access_token]).to be_present
89
+ expect(json_body[:token_type]).to eq 'bearer'
90
+ expect(json_body[:expires_in]).to eq 7200
91
+ expect(json_body[:refresh_token]).to be_nil
92
+ expect(json_body[:scope]).to be_nil
93
+
94
+ expect(last_response.status).to eq 200
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end