doorkeeper 0.5.0 → 0.6.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of doorkeeper might be problematic. Click here for more details.

Files changed (105) hide show
  1. data/.travis.yml +15 -6
  2. data/CHANGELOG.md +19 -1
  3. data/Gemfile +23 -7
  4. data/README.md +62 -27
  5. data/app/controllers/doorkeeper/application_controller.rb +1 -1
  6. data/app/controllers/doorkeeper/authorizations_controller.rb +45 -35
  7. data/app/controllers/doorkeeper/token_info_controller.rb +10 -9
  8. data/app/controllers/doorkeeper/tokens_controller.rb +13 -32
  9. data/app/validators/redirect_uri_validator.rb +11 -0
  10. data/app/views/doorkeeper/applications/_form.html.erb +6 -1
  11. data/app/views/doorkeeper/applications/edit.html.erb +2 -2
  12. data/app/views/doorkeeper/applications/new.html.erb +2 -2
  13. data/app/views/doorkeeper/applications/show.html.erb +4 -1
  14. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  15. data/app/views/doorkeeper/authorizations/new.html.erb +17 -17
  16. data/app/views/doorkeeper/authorizations/show.html.erb +4 -0
  17. data/config/locales/en.yml +10 -0
  18. data/doorkeeper.gemspec +3 -3
  19. data/lib/doorkeeper.rb +11 -2
  20. data/lib/doorkeeper/config.rb +6 -1
  21. data/lib/doorkeeper/errors.rb +15 -0
  22. data/lib/doorkeeper/helpers/controller.rb +24 -0
  23. data/lib/doorkeeper/models/access_grant.rb +1 -1
  24. data/lib/doorkeeper/models/access_token.rb +2 -3
  25. data/lib/doorkeeper/models/active_record/access_token.rb +6 -0
  26. data/lib/doorkeeper/models/mongo_mapper/access_grant.rb +28 -0
  27. data/lib/doorkeeper/models/mongo_mapper/access_token.rb +51 -0
  28. data/lib/doorkeeper/models/mongo_mapper/application.rb +30 -0
  29. data/lib/doorkeeper/models/mongo_mapper/revocable.rb +15 -0
  30. data/lib/doorkeeper/models/{mongoid → mongoid2}/access_grant.rb +1 -1
  31. data/lib/doorkeeper/models/{mongoid → mongoid2}/access_token.rb +6 -0
  32. data/lib/doorkeeper/models/{mongoid → mongoid2}/application.rb +2 -2
  33. data/lib/doorkeeper/models/mongoid3/access_grant.rb +22 -0
  34. data/lib/doorkeeper/models/mongoid3/access_token.rb +41 -0
  35. data/lib/doorkeeper/models/mongoid3/application.rb +22 -0
  36. data/lib/doorkeeper/oauth/authorization/code.rb +9 -17
  37. data/lib/doorkeeper/oauth/authorization/token.rb +8 -18
  38. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +2 -0
  39. data/lib/doorkeeper/oauth/authorization_code_request.rb +82 -0
  40. data/lib/doorkeeper/oauth/client_credentials_request.rb +2 -4
  41. data/lib/doorkeeper/oauth/code_request.rb +28 -0
  42. data/lib/doorkeeper/oauth/code_response.rb +37 -0
  43. data/lib/doorkeeper/oauth/error_response.rb +23 -9
  44. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +4 -0
  45. data/lib/doorkeeper/oauth/password_access_token_request.rb +21 -65
  46. data/lib/doorkeeper/oauth/pre_authorization.rb +62 -0
  47. data/lib/doorkeeper/oauth/refresh_token_request.rb +58 -0
  48. data/lib/doorkeeper/oauth/token_request.rb +28 -0
  49. data/lib/doorkeeper/oauth/token_response.rb +29 -0
  50. data/lib/doorkeeper/rails/routes.rb +4 -3
  51. data/lib/doorkeeper/request.rb +33 -0
  52. data/lib/doorkeeper/request/authorization_code.rb +23 -0
  53. data/lib/doorkeeper/request/client_credentials.rb +23 -0
  54. data/lib/doorkeeper/request/code.rb +24 -0
  55. data/lib/doorkeeper/request/password.rb +23 -0
  56. data/lib/doorkeeper/request/refresh_token.rb +23 -0
  57. data/lib/doorkeeper/request/token.rb +24 -0
  58. data/lib/doorkeeper/server.rb +54 -0
  59. data/lib/doorkeeper/validations.rb +1 -0
  60. data/lib/doorkeeper/version.rb +1 -1
  61. data/lib/generators/doorkeeper/mongo_mapper/indexes_generator.rb +12 -0
  62. data/lib/generators/doorkeeper/templates/README +15 -1
  63. data/lib/generators/doorkeeper/templates/indexes.rb +3 -0
  64. data/lib/generators/doorkeeper/templates/initializer.rb +8 -1
  65. data/script/run_all +9 -9
  66. data/spec/controllers/authorizations_controller_spec.rb +8 -19
  67. data/spec/controllers/token_info_controller_spec.rb +9 -9
  68. data/spec/controllers/tokens_controller_spec.rb +2 -1
  69. data/spec/dummy/app/models/user.rb +11 -4
  70. data/spec/dummy/config/application.rb +8 -1
  71. data/spec/dummy/config/boot.rb +1 -1
  72. data/spec/dummy/config/initializers/doorkeeper.rb +9 -1
  73. data/spec/dummy/config/mongo.yml +11 -0
  74. data/spec/dummy/config/{mongoid.yml → mongoid2.yml} +3 -1
  75. data/spec/dummy/config/mongoid3.yml +18 -0
  76. data/spec/generators/install_generator_spec.rb +1 -0
  77. data/spec/lib/oauth/authorization_code_request_spec.rb +80 -0
  78. data/spec/lib/oauth/client_credentials_request_spec.rb +1 -3
  79. data/spec/lib/oauth/code_request_spec.rb +44 -0
  80. data/spec/lib/oauth/error_response_spec.rb +7 -7
  81. data/spec/lib/oauth/password_access_token_request_spec.rb +30 -143
  82. data/spec/lib/oauth/pre_authorization_spec.rb +80 -0
  83. data/spec/lib/oauth/refresh_token_request_spec.rb +56 -0
  84. data/spec/lib/oauth/token_request_spec.rb +46 -0
  85. data/spec/lib/oauth/{client_credentials/response_spec.rb → token_response_spec.rb} +13 -19
  86. data/spec/lib/server_spec.rb +24 -0
  87. data/spec/requests/endpoints/authorization_spec.rb +11 -27
  88. data/spec/requests/endpoints/token_spec.rb +17 -0
  89. data/spec/requests/flows/authorization_code_errors_spec.rb +0 -45
  90. data/spec/requests/flows/authorization_code_spec.rb +12 -2
  91. data/spec/requests/flows/client_credentials_spec.rb +1 -1
  92. data/spec/requests/flows/password_spec.rb +1 -0
  93. data/spec/requests/flows/refresh_token_spec.rb +6 -4
  94. data/spec/spec_helper_integration.rb +4 -2
  95. data/spec/support/orm/mongo_mapper.rb +26 -0
  96. data/spec/support/orm/mongoid.rb +7 -2
  97. data/spec/validators/redirect_uri_validator_spec.rb +11 -4
  98. metadata +67 -42
  99. data/gemfiles/gemfile.rails-3.1.x +0 -17
  100. data/gemfiles/gemfile.rails-3.2.x +0 -17
  101. data/lib/doorkeeper/oauth/access_token_request.rb +0 -139
  102. data/lib/doorkeeper/oauth/authorization_request.rb +0 -114
  103. data/lib/doorkeeper/oauth/client_credentials/response.rb +0 -42
  104. data/spec/lib/oauth/access_token_request_spec.rb +0 -246
  105. data/spec/lib/oauth/authorization_request_spec.rb +0 -287
@@ -1,7 +1,9 @@
1
1
  development:
2
2
  database: doorkeeper-development
3
3
  persist_in_safe_mode: true
4
+ autocreate_indexes: true
4
5
 
5
6
  test:
6
- database: doorkeeper-test-suite
7
+ database: doorkeeper-mongoid2-test
7
8
  persist_in_safe_mode: true
9
+ autocreate_indexes: true
@@ -0,0 +1,18 @@
1
+ development:
2
+ sessions:
3
+ default:
4
+ database: doorkeeper-mongoid3-development
5
+ hosts:
6
+ - localhost:27017
7
+ options:
8
+ consistency: :strong
9
+ safe: true
10
+ test:
11
+ sessions:
12
+ default:
13
+ database: doorkeeper-mongoid3-test
14
+ hosts:
15
+ - localhost:27017
16
+ options:
17
+ consistency: :strong
18
+ safe: true
@@ -11,6 +11,7 @@ describe 'Doorkeeper::InstallGenerator' do
11
11
  before :each do
12
12
  prepare_destination
13
13
  FileUtils.mkdir(::File.expand_path("config", Pathname(destination_root)))
14
+ FileUtils.mkdir(::File.expand_path("db", Pathname(destination_root)))
14
15
  FileUtils.copy_file(::File.expand_path("../templates/routes.rb", __FILE__), ::File.expand_path("config/routes.rb", Pathname.new(destination_root)))
15
16
  run_generator
16
17
  end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper_integration'
2
+
3
+ module Doorkeeper::OAuth
4
+ describe AuthorizationCodeRequest do
5
+ let(:server) { mock :server, :access_token_expires_in => 2.days, :refresh_token_enabled? => false }
6
+ let(:grant) { FactoryGirl.create :access_grant }
7
+ let(:client) { grant.application }
8
+
9
+ subject do
10
+ AuthorizationCodeRequest.new server, grant, client, :redirect_uri => client.redirect_uri
11
+ end
12
+
13
+ it 'issues a new token for the client' do
14
+ expect do
15
+ subject.authorize
16
+ end.to change { client.access_tokens.count }.by(1)
17
+ end
18
+
19
+ it "issues the token with same grant's scopes" do
20
+ subject.authorize
21
+ Doorkeeper::AccessToken.last.scopes.should == grant.scopes
22
+ end
23
+
24
+ it 'revokes the grant' do
25
+ expect do
26
+ subject.authorize
27
+ end.to change { grant.reload.accessible? }
28
+ end
29
+
30
+ it 'requires the grant to be accessible' do
31
+ grant.revoke
32
+ subject.validate
33
+ subject.error.should == :invalid_grant
34
+ end
35
+
36
+ it 'requires the grant' do
37
+ subject.grant = nil
38
+ subject.validate
39
+ subject.error.should == :invalid_grant
40
+ end
41
+
42
+ it 'requires the client' do
43
+ subject.client = nil
44
+ subject.validate
45
+ subject.error.should == :invalid_client
46
+ end
47
+
48
+ it 'requires the redirect_uri' do
49
+ subject.redirect_uri = nil
50
+ subject.validate
51
+ subject.error.should == :invalid_request
52
+ end
53
+
54
+ it "matches the redirect_uri with grant's one" do
55
+ subject.redirect_uri = 'http://other.com'
56
+ subject.validate
57
+ subject.error.should == :invalid_grant
58
+ end
59
+
60
+ it "matches the client with grant's one" do
61
+ subject.client = FactoryGirl.create :application
62
+ subject.validate
63
+ subject.error.should == :invalid_grant
64
+ end
65
+
66
+ it 'skips token creation if there is a matching one' do
67
+ FactoryGirl.create(:access_token, :application_id => client.id, :resource_owner_id => grant.resource_owner_id, :scopes => "public write")
68
+ expect do
69
+ subject.authorize
70
+ end.to_not change { Doorkeeper::AccessToken.count }
71
+ end
72
+
73
+ it 'revokes matching token if expired' do
74
+ token = FactoryGirl.create(:access_token, :application_id => client.id, :resource_owner_id => grant.resource_owner_id, :scopes => "public write", :expires_in => -100)
75
+ expect do
76
+ subject.authorize
77
+ end.to change { token.reload.revoked? }
78
+ end
79
+ end
80
+ end
@@ -22,7 +22,7 @@ module Doorkeeper::OAuth
22
22
 
23
23
  it 'has successful response when issue was created' do
24
24
  subject.authorize
25
- subject.response.should be_a(ClientCredentialsRequest::Response)
25
+ subject.response.should be_a(TokenResponse)
26
26
  end
27
27
 
28
28
  context 'if issue was not created' do
@@ -30,8 +30,6 @@ module Doorkeeper::OAuth
30
30
  subject.issuer = stub :create => false, :error => :invalid
31
31
  end
32
32
 
33
- its(:authorize) { should be_false }
34
-
35
33
  it 'has an error response' do
36
34
  subject.authorize
37
35
  subject.response.should be_a(Doorkeeper::OAuth::ErrorResponse)
@@ -0,0 +1,44 @@
1
+ require 'spec_helper_integration'
2
+
3
+ module Doorkeeper::OAuth
4
+ describe CodeRequest do
5
+ let(:pre_auth) do
6
+ mock(:pre_auth, {
7
+ :client => mock(:application, :id => 9990),
8
+ :redirect_uri => 'http://tst.com/cb',
9
+ :scopes => nil,
10
+ :state => nil,
11
+ :error => nil,
12
+ :authorizable? => true
13
+ })
14
+ end
15
+
16
+ let(:owner) { mock :owner, :id => 8900 }
17
+
18
+ subject do
19
+ CodeRequest.new(pre_auth, owner)
20
+ end
21
+
22
+ it 'creates an access grant' do
23
+ expect do
24
+ subject.authorize
25
+ end.to change { Doorkeeper::AccessGrant.count }.by(1)
26
+ end
27
+
28
+ it 'returns a code response' do
29
+ subject.authorize.should be_a(CodeResponse)
30
+ end
31
+
32
+ it 'does not create grant when not authorizable' do
33
+ pre_auth.stub :authorizable? => false
34
+ expect do
35
+ subject.authorize
36
+ end.to_not change { Doorkeeper::AccessGrant.count }
37
+ end
38
+
39
+ it 'returns a error response' do
40
+ pre_auth.stub :authorizable? => false
41
+ subject.authorize.should be_a(ErrorResponse)
42
+ end
43
+ end
44
+ end
@@ -24,17 +24,17 @@ module Doorkeeper::OAuth
24
24
  end
25
25
  end
26
26
 
27
- it 'ignores empty attributes' do
27
+ it 'ignores empty error values' do
28
28
  subject = ErrorResponse.new(:error => :some_error, :state => nil)
29
- subject.attributes.should_not have_key(:state)
29
+ subject.body.should_not have_key(:state)
30
30
  end
31
31
 
32
- context 'json response' do
33
- subject { ErrorResponse.new(:name => :some_error, :state => :some_state).as_json }
32
+ describe '.body' do
33
+ subject { ErrorResponse.new(:name => :some_error, :state => :some_state) }
34
34
 
35
- it { should have_key('error') }
36
- it { should have_key('error_description') }
37
- it { should have_key('state') }
35
+ its(:body) { should have_key(:error) }
36
+ its(:body) { should have_key(:error_description) }
37
+ its(:body) { should have_key(:state) }
38
38
  end
39
39
  end
40
40
  end
@@ -2,177 +2,64 @@ require 'spec_helper_integration'
2
2
 
3
3
  module Doorkeeper::OAuth
4
4
  describe PasswordAccessTokenRequest do
5
+ let(:server) { mock :server, :default_scopes => Doorkeeper::OAuth::Scopes.new, :access_token_expires_in => 2.hours, :refresh_token_enabled? => false }
5
6
  let(:client) { FactoryGirl.create(:application) }
6
- let(:owner) { User.create!(:name => "Joe", :password => "sekret") }
7
- let(:params) {
8
- {
9
- :grant_type => "password"
10
- }
11
- }
7
+ let(:owner) { mock :owner, :id => 99 }
12
8
 
13
- describe "with a valid owner and client" do
14
- subject { PasswordAccessTokenRequest.new(client, owner, params) }
15
-
16
- before { subject.authorize }
17
-
18
- it { should be_valid }
19
-
20
- its(:token_type) { should == "bearer" }
21
- its(:error) { should be_nil }
22
- its(:refresh_token) { should be_nil }
23
-
24
- it "has an access token" do
25
- subject.access_token.token.should =~ /\w+/
26
- end
27
- end
28
-
29
- describe "with a valid client but an invalid owner" do
30
- subject { PasswordAccessTokenRequest.new(client, nil, params) }
31
-
32
- before { subject.authorize }
33
-
34
- it { should_not be_valid }
35
- its(:error) { should == :invalid_resource_owner }
36
- its(:access_token) { should be_nil }
37
- its(:refresh_token) { should be_nil }
9
+ subject do
10
+ PasswordAccessTokenRequest.new(server, client, owner)
38
11
  end
39
12
 
40
- describe "with a valid owner but an invalid client" do
41
- subject { PasswordAccessTokenRequest.new(nil, owner, params) }
42
-
43
- before { subject.authorize }
44
-
45
- it { should_not be_valid }
46
- its(:error) { should == :invalid_client }
47
- its(:access_token) { should be_nil }
48
- its(:refresh_token) { should be_nil }
49
- end
50
-
51
- describe "creating the access token" do
52
- subject { PasswordAccessTokenRequest.new(client, owner, params) }
53
-
54
- it "creates with correct params" do
55
- Doorkeeper::AccessToken.should_receive(:create!).with({
56
- :application_id => client.id,
57
- :resource_owner_id => owner.id,
58
- :expires_in => 2.hours,
59
- :scopes =>"",
60
- :use_refresh_token => false,
61
- })
62
- subject.authorize
63
- end
64
-
65
- it "creates a refresh token if Doorkeeper is configured to do so" do
66
- Doorkeeper.configure {
67
- orm DOORKEEPER_ORM
68
- use_refresh_token
69
- }
70
-
71
- Doorkeeper::AccessToken.should_receive(:create!).with({
72
- :application_id => client.id,
73
- :resource_owner_id => owner.id,
74
- :expires_in => 2.hours,
75
- :scopes =>"",
76
- :use_refresh_token => true,
77
- })
13
+ it 'issues a new token for the client' do
14
+ expect do
78
15
  subject.authorize
79
- end
16
+ end.to change { client.access_tokens.count }.by(1)
80
17
  end
81
18
 
82
- describe "with an existing valid access token" do
83
- subject { PasswordAccessTokenRequest.new(client, owner, params) }
84
-
85
- before { subject.authorize }
86
- it { should be_valid }
87
- its(:error) { should be_nil }
88
-
89
- it "will not create a new token" do
90
- subject.should_not_receive(:create_access_token)
91
- subject.authorize
92
- end
19
+ it "requires the owner" do
20
+ subject.resource_owner = nil
21
+ subject.validate
22
+ subject.error.should == :invalid_resource_owner
93
23
  end
94
24
 
95
- describe "with an existing expired access token" do
96
- subject { PasswordAccessTokenRequest.new(client, owner, params) }
97
-
98
- before do
99
- PasswordAccessTokenRequest.new(client, owner, params).authorize
100
- last_token = Doorkeeper::AccessToken.last
101
- # TODO: make this better, maybe with an expire! method?
102
- last_token.update_column :created_at, 10.days.ago
103
- end
104
-
105
- it "will create a new token" do
106
- expect {
107
- subject.authorize
108
- }.to change { Doorkeeper::AccessToken.count }.by(1)
109
- end
25
+ it 'requires the client' do
26
+ subject.client = nil
27
+ subject.validate
28
+ subject.error.should == :invalid_client
110
29
  end
111
30
 
112
- describe "finding the current access token" do
113
- subject { PasswordAccessTokenRequest.new(client, owner, params) }
114
- it { should be_valid }
115
- its(:error) { should be_nil }
116
-
117
- before { subject.authorize }
118
-
119
- it "should find the access token and not create a new one" do
120
- subject.should_not_receive(:create_access_token)
121
- access_token = subject.authorize
122
- subject.access_token.should eq(access_token)
123
- end
31
+ it 'skips token creation if there is already one' do
32
+ FactoryGirl.create(:access_token, :application_id => client.id, :resource_owner_id => owner.id)
33
+ expect do
34
+ subject.authorize
35
+ end.to_not change { Doorkeeper::AccessToken.count }
124
36
  end
125
37
 
126
- describe "creating the first access_token" do
127
- subject { PasswordAccessTokenRequest.new(client, owner, params) }
128
- it { should be_valid }
129
- its(:error) { should be_nil }
130
-
131
- it "should create a new access token" do
132
- subject.should_receive(:create_access_token)
38
+ it 'revokes old token if expired' do
39
+ token = FactoryGirl.create(:access_token, :application_id => client.id, :resource_owner_id => owner.id, :expires_in => -100)
40
+ expect do
133
41
  subject.authorize
134
- end
42
+ end.to change { token.reload.revoked? }
135
43
  end
136
44
 
137
45
  describe "with scopes" do
138
46
  subject do
139
- PasswordAccessTokenRequest.new(client, owner, params.merge(:scope => 'public'))
47
+ PasswordAccessTokenRequest.new(server, client, owner, :scope => 'public')
140
48
  end
141
49
 
142
- before do
143
- Doorkeeper.configure do
144
- orm DOORKEEPER_ORM
145
- default_scopes :public
146
- end
50
+ it 'validates the current scope' do
51
+ server.stub :scopes => Doorkeeper::OAuth::Scopes.from_string('another')
52
+ subject.validate
53
+ subject.error.should == :invalid_scope
147
54
  end
148
55
 
149
56
  it 'creates the token with scopes' do
57
+ server.stub :scopes => Doorkeeper::OAuth::Scopes.from_string("public")
150
58
  expect {
151
59
  subject.authorize
152
60
  }.to change { Doorkeeper::AccessToken.count }.by(1)
153
61
  Doorkeeper::AccessToken.last.scopes.should include(:public)
154
62
  end
155
63
  end
156
-
157
- describe "with errors" do
158
- def token(params)
159
- PasswordAccessTokenRequest.new(client, owner, params)
160
- end
161
-
162
- it "includes the error in the response" do
163
- access_token = token(params.except(:grant_type))
164
- access_token.error_response.name.should == :invalid_request
165
- end
166
-
167
- describe "when client is not present" do
168
- subject { PasswordAccessTokenRequest.new(nil, owner, params) }
169
- its(:error) { should == :invalid_client }
170
- end
171
-
172
- describe "when :grant_type is not 'password'" do
173
- subject { token(params.merge(:grant_type => "invalid")) }
174
- its(:error) { should == :unsupported_grant_type }
175
- end
176
- end
177
64
  end
178
65
  end
@@ -0,0 +1,80 @@
1
+ require "spec_helper_integration"
2
+
3
+ module Doorkeeper::OAuth
4
+ describe PreAuthorization do
5
+ let(:server) { mock :server, :default_scopes => Scopes.new, :scopes => Scopes.from_string('public') }
6
+ let(:client) { mock :client, :redirect_uri => 'http://tst.com/auth' }
7
+
8
+ let :attributes do
9
+ {
10
+ :response_type => 'code',
11
+ :redirect_uri => 'http://tst.com/auth',
12
+ :state => 'save-this'
13
+ }
14
+ end
15
+
16
+ subject do
17
+ PreAuthorization.new(server, client, attributes)
18
+ end
19
+
20
+ it 'is authorizable when request is valid' do
21
+ subject.should be_authorizable
22
+ end
23
+
24
+ it 'accepts code as response type' do
25
+ subject.response_type = 'code'
26
+ subject.should be_authorizable
27
+ end
28
+
29
+ it 'accepts token as response type' do
30
+ subject.response_type = 'token'
31
+ subject.should be_authorizable
32
+ end
33
+
34
+ it 'accepts valid scopes' do
35
+ subject.scope = 'public'
36
+ subject.should be_authorizable
37
+ end
38
+
39
+ it 'uses default scopes when none is required' do
40
+ server.stub :default_scopes => Scopes.from_string('default')
41
+ subject.scope = nil
42
+ subject.scope.should == 'default'
43
+ subject.scopes.should == Scopes.from_string('default')
44
+ end
45
+
46
+ it 'accepts test uri' do
47
+ subject.redirect_uri = 'urn:ietf:wg:oauth:2.0:oob'
48
+ subject.should be_authorizable
49
+ end
50
+
51
+ it "matches the redirect uri against client's one" do
52
+ subject.redirect_uri = 'http://nothesame.com'
53
+ subject.should_not be_authorizable
54
+ end
55
+
56
+ it 'stores the state' do
57
+ subject.state.should == 'save-this'
58
+ end
59
+
60
+ it 'rejects if response type is not allowed' do
61
+ subject.response_type = 'whops'
62
+ subject.should_not be_authorizable
63
+ end
64
+
65
+ it 'requires an existing client' do
66
+ subject.client = nil
67
+ subject.should_not be_authorizable
68
+ end
69
+
70
+ it 'requires a redirect uri' do
71
+ subject.redirect_uri = nil
72
+ subject.should_not be_authorizable
73
+ end
74
+
75
+ it 'rejects non-valid scopes' do
76
+ subject.scope = 'invalid'
77
+ subject.should_not be_authorizable
78
+ end
79
+ end
80
+ end