rockoauth 0.1.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 (54) hide show
  1. checksums.yaml +7 -0
  2. data/History.txt +5 -0
  3. data/README.rdoc +422 -0
  4. data/example/README.rdoc +11 -0
  5. data/example/application.rb +158 -0
  6. data/example/config.ru +3 -0
  7. data/example/environment.rb +11 -0
  8. data/example/models/connection.rb +9 -0
  9. data/example/models/note.rb +4 -0
  10. data/example/models/user.rb +5 -0
  11. data/example/public/style.css +78 -0
  12. data/example/schema.rb +22 -0
  13. data/example/views/authorize.erb +28 -0
  14. data/example/views/create_user.erb +3 -0
  15. data/example/views/error.erb +6 -0
  16. data/example/views/home.erb +24 -0
  17. data/example/views/layout.erb +24 -0
  18. data/example/views/login.erb +20 -0
  19. data/example/views/new_client.erb +25 -0
  20. data/example/views/new_user.erb +22 -0
  21. data/example/views/show_client.erb +15 -0
  22. data/lib/rockoauth/model/authorization.rb +132 -0
  23. data/lib/rockoauth/model/client.rb +54 -0
  24. data/lib/rockoauth/model/client_owner.rb +13 -0
  25. data/lib/rockoauth/model/hashing.rb +26 -0
  26. data/lib/rockoauth/model/helpers.rb +14 -0
  27. data/lib/rockoauth/model/resource_owner.rb +22 -0
  28. data/lib/rockoauth/model.rb +38 -0
  29. data/lib/rockoauth/provider/access_token.rb +70 -0
  30. data/lib/rockoauth/provider/authorization.rb +185 -0
  31. data/lib/rockoauth/provider/error.rb +19 -0
  32. data/lib/rockoauth/provider/exchange.rb +225 -0
  33. data/lib/rockoauth/provider.rb +133 -0
  34. data/lib/rockoauth/router.rb +75 -0
  35. data/lib/rockoauth/schema/20120828112156_rockoauth_schema_original_schema.rb +35 -0
  36. data/lib/rockoauth/schema/20121024180930_rockoauth_schema_add_authorization_index.rb +13 -0
  37. data/lib/rockoauth/schema/20121025180447_rockoauth_schema_add_unique_indexes.rb +31 -0
  38. data/lib/rockoauth/schema.rb +25 -0
  39. data/lib/rockoauth.rb +1 -0
  40. data/spec/factories.rb +20 -0
  41. data/spec/request_helpers.rb +62 -0
  42. data/spec/rockoauth/model/authorization_spec.rb +237 -0
  43. data/spec/rockoauth/model/client_spec.rb +44 -0
  44. data/spec/rockoauth/model/helpers_spec.rb +25 -0
  45. data/spec/rockoauth/model/resource_owner_spec.rb +87 -0
  46. data/spec/rockoauth/provider/access_token_spec.rb +138 -0
  47. data/spec/rockoauth/provider/authorization_spec.rb +356 -0
  48. data/spec/rockoauth/provider/exchange_spec.rb +361 -0
  49. data/spec/rockoauth/provider_spec.rb +560 -0
  50. data/spec/spec_helper.rb +80 -0
  51. data/spec/test_app/helper.rb +36 -0
  52. data/spec/test_app/provider/application.rb +67 -0
  53. data/spec/test_app/provider/views/authorize.erb +19 -0
  54. metadata +238 -0
@@ -0,0 +1,31 @@
1
+ class RockoauthSchemaAddUniqueIndexes < ActiveRecord::Migration
2
+ FIELDS = [:code, :refresh_token_hash]
3
+
4
+ def self.up
5
+ FIELDS.each do |field|
6
+ remove_index :oauth2_authorizations, :name => "index_oauth2_client_id_and_#{field}"
7
+ add_index :oauth2_authorizations, [:client_id, field], :unique => true, :name => "index_oauth2_client_id_#{field}"
8
+ end
9
+ remove_index :oauth2_authorizations, [:access_token_hash]
10
+ add_index :oauth2_authorizations, [:access_token_hash], :unique => true
11
+
12
+ remove_index :oauth2_clients, [:client_id]
13
+ add_index :oauth2_clients, [:client_id], :unique => true
14
+
15
+ add_index :oauth2_clients, [:name], :unique => true
16
+ end
17
+
18
+ def self.down
19
+ FIELDS.each do |field|
20
+ remove_index :oauth2_authorizations, [:client_id, field]
21
+ add_index :oauth2_authorizations, [:client_id, field]
22
+ end
23
+ remove_index :oauth2_authorizations, [:access_token_hash]
24
+ add_index :oauth2_authorizations, [:access_token_hash]
25
+
26
+ remove_index :oauth2_clients, [:client_id]
27
+ add_index :oauth2_clients, [:client_id]
28
+
29
+ remove_index :oauth2_clients, [:name]
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ module RockOAuth
2
+
3
+ class Schema
4
+ def self.migrate
5
+ ActiveRecord::Base.logger ||= Logger.new(StringIO.new)
6
+ ActiveRecord::Migrator.up(migrations_path)
7
+ end
8
+ class << self
9
+ alias :up :migrate
10
+ end
11
+
12
+ def self.rollback
13
+ ActiveRecord::Base.logger ||= Logger.new(StringIO.new)
14
+ ActiveRecord::Migrator.down(migrations_path)
15
+ end
16
+ class << self
17
+ alias :down :rollback
18
+ end
19
+
20
+ def self.migrations_path
21
+ File.expand_path('../schema', __FILE__)
22
+ end
23
+ end
24
+
25
+ end
data/lib/rockoauth.rb ADDED
@@ -0,0 +1 @@
1
+ require 'rockoauth/provider'
data/spec/factories.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'factory_girl'
2
+
3
+ Factory.sequence :client_name do |n|
4
+ "Client ##{n}"
5
+ end
6
+
7
+ Factory.sequence :user_name do |n|
8
+ "User ##{n}"
9
+ end
10
+
11
+ Factory.define :owner, :class => TestApp::User do |u|
12
+ u.name { Factory.next :user_name }
13
+ end
14
+
15
+ Factory.define :client, :class => RockOAuth::Model::Client do |c|
16
+ c.client_id { RockOAuth.random_string }
17
+ c.client_secret { RockOAuth.random_string }
18
+ c.name { Factory.next :client_name }
19
+ c.redirect_uri 'https://client.example.com/cb'
20
+ end
@@ -0,0 +1,62 @@
1
+ require "socket"
2
+
3
+ module RequestHelpers
4
+ require "net/http"
5
+
6
+ SERVER_PORT = TCPServer.new(0).addr[1]
7
+
8
+ def querystring(params)
9
+ params.map { |k,v| "#{ CGI.escape k.to_s }=#{ CGI.escape v.to_s }" }.join("&")
10
+ end
11
+
12
+ def get(query_params)
13
+ uri = URI.parse("http://localhost:#{SERVER_PORT}/authorize?" + querystring(query_params))
14
+ Net::HTTP.get_response(uri)
15
+ end
16
+
17
+ def allow_or_deny(query_params)
18
+ Net::HTTP.post_form(URI.parse("http://localhost:#{SERVER_PORT}/allow"), query_params)
19
+ end
20
+
21
+ def post_basic_auth(auth_params, query_params)
22
+ url = "http://#{ auth_params["client_id"] }:#{ auth_params["client_secret"] }@localhost:#{SERVER_PORT}/authorize"
23
+ Net::HTTP.post_form(URI.parse(url), query_params)
24
+ end
25
+
26
+ def post(body_params, query_params = {})
27
+ uri = URI.parse("http://localhost:#{SERVER_PORT}/authorize?" + querystring(query_params))
28
+ Net::HTTP.start(uri.host, uri.port) do |http|
29
+ http.post(uri.path + "?" + uri.query.to_s, querystring(body_params))
30
+ end
31
+ end
32
+
33
+ def validate_response(response, status, body)
34
+ expect(response.code.to_i).to eq(status)
35
+ expect(response.body).to eq(body)
36
+ expect(response["Cache-Control"]).to eq("no-store")
37
+ end
38
+
39
+ def validate_json_response(response, status, body)
40
+ expect(response.code.to_i).to eq(status)
41
+ expect(JSON.parse(response.body)).to eq(body)
42
+ expect(response["Content-Type"]).to eq("application/json")
43
+ expect(response["Cache-Control"]).to eq("no-store")
44
+ end
45
+
46
+ def mock_request(request_class, stubs = {})
47
+ mock_request = double(request_class)
48
+ method_stubs = {
49
+ :redirect? => false,
50
+ :response_body => nil,
51
+ :response_headers => {},
52
+ :response_status => 200
53
+ }.merge(stubs)
54
+
55
+ method_stubs.each do |method, value|
56
+ expect(mock_request).to receive(method).and_return(value)
57
+ end
58
+
59
+ mock_request
60
+ end
61
+ end
62
+
@@ -0,0 +1,237 @@
1
+ require 'spec_helper'
2
+
3
+ describe RockOAuth::Model::Authorization do
4
+ let(:client) { Factory :client }
5
+ let(:impostor) { Factory :client }
6
+ let(:owner) { Factory :owner }
7
+ let(:user) { Factory :owner }
8
+ let(:tester) { Factory(:owner) }
9
+
10
+ let(:authorization) do
11
+ create_authorization(:owner => tester, :client => client)
12
+ end
13
+
14
+ it "is valid" do
15
+ expect(authorization).to be_valid
16
+ end
17
+
18
+ it "is not valid without a client" do
19
+ authorization.client = nil
20
+ expect(authorization).not_to be_valid
21
+ end
22
+
23
+ it "is not valid without an owner" do
24
+ authorization.owner = nil
25
+ expect(authorization).not_to be_valid
26
+ end
27
+
28
+ describe "when there are existing authorizations" do
29
+ before do
30
+ create_authorization(
31
+ :owner => user,
32
+ :client => impostor,
33
+ :access_token => 'existing_access_token')
34
+
35
+ create_authorization(
36
+ :owner => user,
37
+ :client => client,
38
+ :code => 'existing_code')
39
+
40
+ create_authorization(
41
+ :owner => owner,
42
+ :client => client,
43
+ :refresh_token => 'existing_refresh_token')
44
+ end
45
+
46
+ it "is valid if its access_token is unique" do
47
+ expect(authorization).to be_valid
48
+ end
49
+
50
+ it "is valid if both access_tokens are nil" do
51
+ RockOAuth::Model::Authorization.first.update_attribute(:access_token, nil)
52
+ authorization.access_token = nil
53
+ expect(authorization).to be_valid
54
+ end
55
+
56
+ it "is not valid if its access_token is not unique" do
57
+ authorization.access_token = 'existing_access_token'
58
+ expect(authorization).not_to be_valid
59
+ end
60
+
61
+ it "is valid if it has a unique code for its client" do
62
+ authorization.client = impostor
63
+ authorization.code = 'existing_code'
64
+ expect(authorization).to be_valid
65
+ end
66
+
67
+ it "is not valid if it does not have a unique client and code" do
68
+ authorization.code = 'existing_code'
69
+ expect(authorization).not_to be_valid
70
+ end
71
+
72
+ it "is valid if it has a unique refresh_token for its client" do
73
+ authorization.client = impostor
74
+ authorization.refresh_token = 'existing_refresh_token'
75
+ expect(authorization).to be_valid
76
+ end
77
+
78
+ it "is not valid if it does not have a unique client and refresh_token" do
79
+ authorization.refresh_token = 'existing_refresh_token'
80
+ expect(authorization).not_to be_valid
81
+ end
82
+
83
+ describe ".create_code" do
84
+ before { allow(RockOAuth).to receive(:random_string).and_return('existing_code', 'new_code') }
85
+
86
+ it "returns the first code the client has not used" do
87
+ expect(RockOAuth::Model::Authorization.create_code(client)).to eq('new_code')
88
+ end
89
+
90
+ it "returns the first code another client has not used" do
91
+ expect(RockOAuth::Model::Authorization.create_code(impostor)).to eq('existing_code')
92
+ end
93
+ end
94
+
95
+ describe ".create_access_token" do
96
+ before { allow(RockOAuth).to receive(:random_string).and_return('existing_access_token', 'new_access_token') }
97
+
98
+ it "returns the first unused token it can find" do
99
+ expect(RockOAuth::Model::Authorization.create_access_token).to eq('new_access_token')
100
+ end
101
+ end
102
+
103
+ describe ".create_refresh_token" do
104
+ before { allow(RockOAuth).to receive(:random_string).and_return('existing_refresh_token', 'new_refresh_token') }
105
+
106
+ it "returns the first refresh_token the client has not used" do
107
+ expect(RockOAuth::Model::Authorization.create_refresh_token(client)).to eq('new_refresh_token')
108
+ end
109
+
110
+ it "returns the first refresh_token another client has not used" do
111
+ expect(RockOAuth::Model::Authorization.create_refresh_token(impostor)).to eq('existing_refresh_token')
112
+ end
113
+ end
114
+
115
+ describe "duplicate records" do
116
+ it "raises an error if a duplicate authorization is created" do
117
+ expect {
118
+ authorization = RockOAuth::Model::Authorization.__send__(:new)
119
+ authorization.owner = user
120
+ authorization.client = client
121
+ authorization.save
122
+ }.to raise_error
123
+ end
124
+
125
+ it "finds an existing record after a race" do
126
+ allow(user).to receive(:oauth2_authorization_for) do
127
+ allow(user).to receive(:oauth2_authorization_for).and_call_original
128
+ raise TypeError, 'Mysql::Error: Duplicate entry'
129
+ end
130
+ authorization = RockOAuth::Model::Authorization.for(user, client)
131
+ expect(authorization.owner).to eq(user)
132
+ expect(authorization.client).to eq(client)
133
+ end
134
+ end
135
+ end
136
+
137
+ describe "#exchange!" do
138
+ it "saves the record" do
139
+ expect(authorization).to receive(:save!)
140
+ authorization.exchange!
141
+ end
142
+
143
+ it "uses its helpers to find unique tokens" do
144
+ expect(RockOAuth::Model::Authorization).to receive(:create_access_token).and_return('access_token')
145
+ authorization.exchange!
146
+ expect(authorization.access_token).to eq('access_token')
147
+ end
148
+
149
+ it "updates the tokens correctly" do
150
+ authorization.exchange!
151
+ expect(authorization).to be_valid
152
+ expect(authorization.code).to be_nil
153
+ expect(authorization.refresh_token).to be_nil
154
+ end
155
+ end
156
+
157
+ describe "#expired?" do
158
+ it "returns false when not expiry is set" do
159
+ expect(authorization).not_to be_expired
160
+ end
161
+
162
+ it "returns false when expiry is in the future" do
163
+ authorization.expires_at = 2.days.from_now
164
+ expect(authorization).not_to be_expired
165
+ end
166
+
167
+ it "returns true when expiry is in the past" do
168
+ authorization.expires_at = 2.days.ago
169
+ expect(authorization).to be_expired
170
+ end
171
+ end
172
+
173
+ describe "#grants_access?" do
174
+ it "returns true given the right user" do
175
+ expect(authorization.grants_access?(tester)).to eq(true)
176
+ end
177
+
178
+ it "returns false given the wrong user" do
179
+ expect(authorization.grants_access?(user)).to eq(false)
180
+ end
181
+
182
+ describe "when the authorization is expired" do
183
+ before { authorization.expires_at = 2.days.ago }
184
+
185
+ it "returns false in all cases" do
186
+ expect(authorization.grants_access?(tester)).to eq(false)
187
+ expect(authorization.grants_access?(user)).to eq(false)
188
+ end
189
+ end
190
+ end
191
+
192
+ describe "with a scope" do
193
+ before { authorization.scope = 'foo bar' }
194
+
195
+ describe "#in_scope?" do
196
+ it "returns true for authorized scopes" do
197
+ expect(authorization).to be_in_scope('foo')
198
+ expect(authorization).to be_in_scope('bar')
199
+ end
200
+
201
+ it "returns false for unauthorized scopes" do
202
+ expect(authorization).not_to be_in_scope('qux')
203
+ expect(authorization).not_to be_in_scope('fo')
204
+ end
205
+ end
206
+
207
+ describe "#grants_access?" do
208
+ it "returns true given the right user and all authorization scopes" do
209
+ expect(authorization.grants_access?(tester, 'foo', 'bar')).to eq(true)
210
+ end
211
+
212
+ it "returns true given the right user and some authorization scopes" do
213
+ expect(authorization.grants_access?(tester, 'bar')).to eq(true)
214
+ end
215
+
216
+ it "returns false given the right user and some unauthorization scopes" do
217
+ expect(authorization.grants_access?(tester, 'foo', 'bar', 'qux')).to eq(false)
218
+ end
219
+
220
+ it "returns false given an unauthorized scope" do
221
+ expect(authorization.grants_access?(tester, 'qux')).to eq(false)
222
+ end
223
+
224
+ it "returns true given the right user" do
225
+ expect(authorization.grants_access?(tester)).to eq(true)
226
+ end
227
+
228
+ it "returns false given the wrong user" do
229
+ expect(authorization.grants_access?(user)).to eq(false)
230
+ end
231
+
232
+ it "returns false given the wrong user and an authorized scope" do
233
+ expect(authorization.grants_access?(user, 'foo')).to eq(false)
234
+ end
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe RockOAuth::Model::Client do
4
+ before do
5
+ @client = RockOAuth::Model::Client.create(:name => 'App', :redirect_uri => 'http://example.com/cb')
6
+ @owner = Factory(:owner)
7
+ RockOAuth::Model::Authorization.for(@owner, @client)
8
+ end
9
+
10
+ it "is valid" do
11
+ expect(@client).to be_valid
12
+ end
13
+
14
+ it "is invalid without a name" do
15
+ @client.name = nil
16
+ expect(@client).not_to be_valid
17
+ end
18
+
19
+ it "is invalid without a redirect_uri" do
20
+ @client.redirect_uri = nil
21
+ expect(@client).not_to be_valid
22
+ end
23
+
24
+ it "is invalid with a non-URI redirect_uri" do
25
+ @client.redirect_uri = 'foo'
26
+ expect(@client).not_to be_valid
27
+ end
28
+
29
+ # http://en.wikipedia.org/wiki/HTTP_response_splitting
30
+ it "is invalid if the URI contains HTTP line breaks" do
31
+ @client.redirect_uri = "http://example.com/c\r\nb"
32
+ expect(@client).not_to be_valid
33
+ end
34
+
35
+ it "has client_id and client_secret filled in" do
36
+ expect(@client.client_id).not_to be_nil
37
+ expect(@client.client_secret).not_to be_nil
38
+ end
39
+
40
+ it "destroys its authorizations on destroy" do
41
+ @client.destroy
42
+ expect(RockOAuth::Model::Authorization.count).to be_zero
43
+ end
44
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe RockOAuth::Model::Helpers do
4
+ subject { RockOAuth::Model::Helpers }
5
+
6
+ describe '.count' do
7
+ let(:owner) { Factory(:owner) }
8
+
9
+ before do
10
+ 3.times { Factory(:client, :owner => owner) }
11
+ end
12
+
13
+ context 'when conditions are not passed' do
14
+ it 'returns count of total rows' do
15
+ expect(subject.count(owner.oauth2_clients)).to eq(3)
16
+ end
17
+ end
18
+
19
+ context 'when conditions are passed' do
20
+ it 'returns count of rows satisfying supplied conditions' do
21
+ expect(subject.count(RockOAuth::Model::Client, :client_id => RockOAuth::Model::Client.first.client_id)).to eq(1)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ describe RockOAuth::Model::ResourceOwner do
4
+ before do
5
+ @owner = Factory(:owner)
6
+ @client = Factory(:client)
7
+ end
8
+
9
+ describe "#grant_access!" do
10
+ it "raises an error when passed an invalid client argument" do
11
+ expect{ @owner.grant_access!('client') }.to raise_error(ArgumentError)
12
+ end
13
+
14
+ it "creates an authorization between the owner and the client" do
15
+ authorization = RockOAuth::Model::Authorization.__send__(:new)
16
+ expect(RockOAuth::Model::Authorization).to receive(:new).and_return(authorization)
17
+ @owner.grant_access!(@client)
18
+ end
19
+
20
+ # This is hacky, but doubleing ActiveRecord turns out to get messy
21
+ it "creates an Authorization" do
22
+ expect(RockOAuth::Model::Authorization.count).to eq(0)
23
+ @owner.grant_access!(@client)
24
+ expect(RockOAuth::Model::Authorization.count).to eq(1)
25
+ end
26
+
27
+ it "returns the authorization" do
28
+ expect(@owner.grant_access!(@client)).to be_kind_of(RockOAuth::Model::Authorization)
29
+ end
30
+
31
+ # This method must return the same owner object, since the assertion
32
+ # handler may modify it -- either by changing its attributes or by extending
33
+ # it with new methods. These changes must be returned to the app calling the
34
+ # Provider interface.
35
+ it "sets the receiver as the authorization's owner" do
36
+ authorization = @owner.grant_access!(@client)
37
+ expect(authorization.owner).to be_equal(@owner)
38
+ end
39
+
40
+ it "sets the duration of the authorization" do
41
+ authorization = @owner.grant_access!(@client, :duration => 5.hours)
42
+ expect(authorization.expires_at.to_i).to eq((Time.now + 5.hours.to_i).to_i)
43
+ end
44
+ end
45
+
46
+ describe "when there is an existing authorization" do
47
+ before do
48
+ @authorization = create_authorization(:owner => @owner, :client => @client)
49
+ end
50
+
51
+ it "does not create a new one" do
52
+ expect(RockOAuth::Model::Authorization).not_to receive(:new)
53
+ @owner.grant_access!(@client)
54
+ end
55
+
56
+ it "updates the authorization with scopes" do
57
+ @owner.grant_access!(@client, :scopes => ['foo', 'bar'])
58
+ @authorization.reload
59
+ expect(@authorization.scopes).to eq(Set.new(['foo', 'bar']))
60
+ end
61
+
62
+ describe "with scopes" do
63
+ before do
64
+ @authorization.update_attribute(:scope, 'foo bar')
65
+ end
66
+
67
+ it "merges the new scopes with the existing ones" do
68
+ @owner.grant_access!(@client, :scopes => ['qux'])
69
+ @authorization.reload
70
+ expect(@authorization.scopes).to eq(Set.new(['foo', 'bar', 'qux']))
71
+ end
72
+
73
+ it "does not add duplicate scopes to the list" do
74
+ @owner.grant_access!(@client, :scopes => ['qux'])
75
+ @owner.grant_access!(@client, :scopes => ['qux'])
76
+ @authorization.reload
77
+ expect(@authorization.scopes).to eq(Set.new(['foo', 'bar', 'qux']))
78
+ end
79
+ end
80
+ end
81
+
82
+ it "destroys its authorizations on destroy" do
83
+ RockOAuth::Model::Authorization.for(@owner, @client)
84
+ @owner.destroy
85
+ expect(RockOAuth::Model::Authorization.count).to be_zero
86
+ end
87
+ end
@@ -0,0 +1,138 @@
1
+ require 'spec_helper'
2
+
3
+ describe RockOAuth::Provider::AccessToken do
4
+ before do
5
+ @alice = TestApp::User['Alice']
6
+ @bob = TestApp::User['Bob']
7
+
8
+ create_authorization(
9
+ :owner => @alice,
10
+ :client => Factory(:client),
11
+ :scope => 'profile',
12
+ :access_token => 'sesame')
13
+
14
+ @authorization = create_authorization(
15
+ :owner => @bob,
16
+ :client => Factory(:client),
17
+ :scope => 'profile',
18
+ :access_token => 'magic-key')
19
+
20
+ RockOAuth::Provider.realm = 'Demo App'
21
+ end
22
+
23
+ let :token do
24
+ RockOAuth::Provider::AccessToken.new(@bob, ['profile'], 'magic-key')
25
+ end
26
+
27
+ shared_examples_for "valid token" do
28
+ it "is valid" do
29
+ expect(token).to be_valid
30
+ end
31
+ it "does not add headers" do
32
+ expect(token.response_headers).to eq({})
33
+ end
34
+ it "has an OK status code" do
35
+ expect(token.response_status).to eq(200)
36
+ end
37
+ it "returns the owner who granted the authorization" do
38
+ expect(token.owner).to eq(@bob)
39
+ end
40
+ end
41
+
42
+ shared_examples_for "invalid token" do
43
+ it "is not valid" do
44
+ expect(token).not_to be_valid
45
+ end
46
+ it "does not return the owner" do
47
+ expect(token.owner).to be_nil
48
+ end
49
+ end
50
+
51
+ describe "with the right user, scope and token" do
52
+ it_should_behave_like "valid token"
53
+ end
54
+
55
+ describe "with an implicit user" do
56
+ let :token do
57
+ RockOAuth::Provider::AccessToken.new(:implicit, ['profile'], 'magic-key')
58
+ end
59
+ it_should_behave_like "valid token"
60
+ end
61
+
62
+ describe "with no user" do
63
+ let :token do
64
+ RockOAuth::Provider::AccessToken.new(nil, ['profile'], 'magic-key')
65
+ end
66
+ it_should_behave_like "invalid token"
67
+
68
+ it "returns an error response" do
69
+ expect(token.response_headers['WWW-Authenticate']).to eq("OAuth realm='Demo App', error='invalid_token'")
70
+ expect(token.response_status).to eq(401)
71
+ end
72
+ end
73
+
74
+ describe "with less scope than was granted" do
75
+ let :token do
76
+ RockOAuth::Provider::AccessToken.new(@bob, [], 'magic-key')
77
+ end
78
+ it_should_behave_like "valid token"
79
+ end
80
+
81
+ describe "when the authorization has expired" do
82
+ before { @authorization.update_attribute(:expires_at, 1.hour.ago) }
83
+ it_should_behave_like "invalid token"
84
+
85
+ it "returns an error response" do
86
+ expect(token.response_headers['WWW-Authenticate']).to eq("OAuth realm='Demo App', error='expired_token'")
87
+ expect(token.response_status).to eq(401)
88
+ end
89
+ end
90
+
91
+ describe "with a non-existent token" do
92
+ let :token do
93
+ RockOAuth::Provider::AccessToken.new(@bob, ['profile'], 'is-the-password-books')
94
+ end
95
+ it_should_behave_like "invalid token"
96
+
97
+ it "returns an error response" do
98
+ expect(token.response_headers['WWW-Authenticate']).to eq("OAuth realm='Demo App', error='invalid_token'")
99
+ expect(token.response_status).to eq(401)
100
+ end
101
+ end
102
+
103
+ describe "with a token for the wrong user" do
104
+ let :token do
105
+ RockOAuth::Provider::AccessToken.new(@bob, ['profile'], 'sesame')
106
+ end
107
+ it_should_behave_like "invalid token"
108
+
109
+ it "returns an error response" do
110
+ expect(token.response_headers['WWW-Authenticate']).to eq("OAuth realm='Demo App', error='insufficient_scope'")
111
+ expect(token.response_status).to eq(403)
112
+ end
113
+ end
114
+
115
+ describe "with a token for an ungranted scope" do
116
+ let :token do
117
+ RockOAuth::Provider::AccessToken.new(@bob, ['offline_access'], 'magic-key')
118
+ end
119
+ it_should_behave_like "invalid token"
120
+
121
+ it "returns an error response" do
122
+ expect(token.response_headers['WWW-Authenticate']).to eq("OAuth realm='Demo App', error='insufficient_scope'")
123
+ expect(token.response_status).to eq(403)
124
+ end
125
+ end
126
+
127
+ describe "with no token string" do
128
+ let :token do
129
+ RockOAuth::Provider::AccessToken.new(@bob, ['profile'], nil)
130
+ end
131
+ it_should_behave_like "invalid token"
132
+
133
+ it "returns an error response" do
134
+ expect(token.response_headers['WWW-Authenticate']).to eq("OAuth realm='Demo App'")
135
+ expect(token.response_status).to eq(401)
136
+ end
137
+ end
138
+ end