warden-oauth2-strategies 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +13 -0
- data/Guardfile +11 -0
- data/README.md +196 -0
- data/Rakefile +8 -0
- data/lib/warden/oauth2/error_app.rb +23 -0
- data/lib/warden/oauth2/failure_app.rb +22 -0
- data/lib/warden/oauth2/strategies/base.rb +17 -0
- data/lib/warden/oauth2/strategies/bearer.rb +30 -0
- data/lib/warden/oauth2/strategies/client.rb +57 -0
- data/lib/warden/oauth2/strategies/client_credentials.rb +16 -0
- data/lib/warden/oauth2/strategies/public.rb +21 -0
- data/lib/warden/oauth2/strategies/resource_owner_password_credentials.rb +28 -0
- data/lib/warden/oauth2/strategies/token.rb +40 -0
- data/lib/warden/oauth2/version.rb +5 -0
- data/lib/warden/oauth2.rb +35 -0
- data/lib/warden-oauth2.rb +1 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/warden/oauth2/failure_app_spec.rb +30 -0
- data/spec/warden/oauth2/strategies/bearer_spec.rb +32 -0
- data/spec/warden/oauth2/strategies/client_credentials_spec.rb +28 -0
- data/spec/warden/oauth2/strategies/client_spec.rb +73 -0
- data/spec/warden/oauth2/strategies/public_spec.rb +26 -0
- data/spec/warden/oauth2/strategies/resource_owner_password_credentials_spec.rb +64 -0
- data/spec/warden/oauth2/strategies/token_spec.rb +56 -0
- data/warden-oauth2.gemspec +22 -0
- metadata +135 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9e885487f2c3e237d8a7cffa9020ea86671d015e
|
4
|
+
data.tar.gz: 9b6d3b51466a0861cf876fa606d407fd9fd2da18
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f3fd02b16da09b9454a707f3d8843e969d907239f824caa136892c80734e63ac37b20befd927c4b5b6985fa87ca0dfcaca25228bd5c8672c8d57c3bbe743b69d
|
7
|
+
data.tar.gz: 4a1ddb4c062dec57fbb06ea3e3a7d8da582baba7063d267bef97d2e404f0fa35b07d1ff07955d73a612bc21065e9fb55d46503c0c95d92ee8a5a8475eaa538f5
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
# Warden::OAuth2 [](http://travis-ci.org/opperator/warden-oauth2)
|
2
|
+
|
3
|
+
This is a fork of [original project](https://github.com/opperator/warden-oauth2) which is actually maintained.
|
4
|
+
This library provides a robust set of authorization strategies for
|
5
|
+
Warden meant to be used to implement an OAuth 2.0 (targeting RFC6749)
|
6
|
+
provider.
|
7
|
+
|
8
|
+
## Usage
|
9
|
+
|
10
|
+
### Grape API Example
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
require 'grape'
|
14
|
+
require 'warden-oauth2'
|
15
|
+
|
16
|
+
class MyAPI < Grape::API
|
17
|
+
use Warden::Manager do |config|
|
18
|
+
strategies.add :bearer, Warden::OAuth2::Strategies::Bearer
|
19
|
+
strategies.add :client_credentials, Warden::OAuth2::Strategies::ClientCredentials
|
20
|
+
strategies.add :resource_owner_password_credentials, Warden::OAuth2::Strategies::ResourceOwnerPasswordCredentials
|
21
|
+
strategies.add :public, Warden::OAuth2::Strategies::Public
|
22
|
+
|
23
|
+
config.default_strategies :bearer, :client_credentials, :resource_owner_password_credentials, :public
|
24
|
+
config.failure_app Warden::OAuth2::FailureApp
|
25
|
+
end
|
26
|
+
|
27
|
+
helpers do
|
28
|
+
def warden; env['warden'] end
|
29
|
+
end
|
30
|
+
|
31
|
+
resources :hamburgers do
|
32
|
+
before do
|
33
|
+
warden.authenticate! scope: :hamburgers
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
## Configuration
|
40
|
+
|
41
|
+
You can configure Warden::OAuth2 like so:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
Warden::OAuth2.configure do |config|
|
45
|
+
config.some_option = some_value
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
### Configurable Options
|
50
|
+
|
51
|
+
* **client_credentials_model:** A client application class used for client credentials authentication. See **Models** below.
|
52
|
+
Defaults to `ClientCredentialsApplication`.
|
53
|
+
* **resource_owner_password_credentials_model:** A client application class used for resource owner password authentication. See **Models** below.
|
54
|
+
Defaults to `ResourceOwnerPasswordCredentialsApplication`.
|
55
|
+
* **token_model:** An access token class. See **Models** below. Defaults
|
56
|
+
to `AccessToken`.
|
57
|
+
|
58
|
+
## Models
|
59
|
+
|
60
|
+
You will need to supply data models to back up the persistent facets of
|
61
|
+
your OAuth 2.0 implementation. Below are examples of the interfaces that
|
62
|
+
each require.
|
63
|
+
|
64
|
+
### Client Credentials Application
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
class ClientCredentialsApplication
|
68
|
+
# REQUIRED
|
69
|
+
def self.locate(client_id, client_secret = nil)
|
70
|
+
# Should return a client application matching the client_id
|
71
|
+
# provided, but should ONLY match client_secret if it is
|
72
|
+
# provided.
|
73
|
+
end
|
74
|
+
|
75
|
+
# OPTIONAL
|
76
|
+
def scope?(scope)
|
77
|
+
# True if the client should be able to access the scope passed
|
78
|
+
# (usually a symbol) without having an access token.
|
79
|
+
end
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
### Resource Owner Password Credentials Application
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
class ResourceOwnerPasswordCredentialsApplication
|
87
|
+
# REQUIRED
|
88
|
+
def self.locate(client_id, client_secret = nil)
|
89
|
+
# Should return a client application matching the client_id
|
90
|
+
# provided, but should ONLY match client_secret if it is
|
91
|
+
# provided.
|
92
|
+
end
|
93
|
+
#REQUIRED
|
94
|
+
def valid?(options={})
|
95
|
+
# Use options[:username] and options[:password] to check
|
96
|
+
# that specified credentials are valid
|
97
|
+
end
|
98
|
+
|
99
|
+
# OPTIONAL
|
100
|
+
def scope?(scope)
|
101
|
+
# True if the client should be able to access the scope passed
|
102
|
+
# (usually a symbol) without having an access token.
|
103
|
+
end
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
### Access Token
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
class AccessToken
|
111
|
+
# REQUIRED
|
112
|
+
def self.locate(token_string)
|
113
|
+
# Should return an access token matching the string provided.
|
114
|
+
# Note that you MAY include expired access tokens in the result
|
115
|
+
# of this method so long as you implement an instance #expired?
|
116
|
+
# method.
|
117
|
+
end
|
118
|
+
|
119
|
+
# OPTIONAL
|
120
|
+
def expired?
|
121
|
+
# True if the access token has reached its expiration.
|
122
|
+
end
|
123
|
+
|
124
|
+
# OPTIONAL
|
125
|
+
def scope?(scope)
|
126
|
+
# True if the scope passed in (usually a symbol) has been authorized
|
127
|
+
# for this access token.
|
128
|
+
end
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
## Strategies
|
133
|
+
|
134
|
+
### Bearer
|
135
|
+
|
136
|
+
This strategy authenticates by trying to find an access token that is
|
137
|
+
supplied according to the OAuth 2.0 Bearer Token specification
|
138
|
+
([draft 8][oauth2-bearer]). It does this by first extracting the access
|
139
|
+
token in string form and then calling the `.locate` method on your
|
140
|
+
access token model (see **Configuration** above).
|
141
|
+
|
142
|
+
Token-based strategies will also fail if they are expired or lack
|
143
|
+
sufficient scope. See **Models** above.
|
144
|
+
|
145
|
+
**User:** The Warden user is set to the client application returned by
|
146
|
+
`.locate`.
|
147
|
+
|
148
|
+
### Client credentials
|
149
|
+
|
150
|
+
This strategy authenticates an OAuth 2.0 client application directly for
|
151
|
+
endpoints that don't require a specific user. You might use this
|
152
|
+
strategy when you want to create an API for client statistics or if you
|
153
|
+
wish to rate limit based on a client application even for publicly
|
154
|
+
accessible endpoints.
|
155
|
+
|
156
|
+
**User:** The Warden user is set to the access token returned by `.locate`.
|
157
|
+
|
158
|
+
### Resource Owner Password Credential
|
159
|
+
|
160
|
+
This strategy creates an access token for a user with matching credentials.
|
161
|
+
Use `.valid?` on the client application to determine if user credentials are correct.
|
162
|
+
|
163
|
+
**User:** The Warden user is set to the access token returned by `.locate`.
|
164
|
+
|
165
|
+
### Public
|
166
|
+
|
167
|
+
This strategy succeeds by default and only fails if the authentication
|
168
|
+
scope is set and is something other than `:public`.
|
169
|
+
|
170
|
+
**User:** The Warden user is set to `nil`.
|
171
|
+
|
172
|
+
[oauth2]: http://tools.ietf.org/html/draft-ietf-oauth-v2-22
|
173
|
+
[oauth2-bearer]: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-08
|
174
|
+
|
175
|
+
## License
|
176
|
+
The MIT License
|
177
|
+
|
178
|
+
Copyright (c) 2014 AirService Pty Ltd. http://www.airservice.com
|
179
|
+
|
180
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
181
|
+
of this software and associated documentation files (the "Software"), to deal
|
182
|
+
in the Software without restriction, including without limitation the rights
|
183
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
184
|
+
copies of the Software, and to permit persons to whom the Software is
|
185
|
+
furnished to do so, subject to the following conditions:
|
186
|
+
|
187
|
+
The above copyright notice and this permission notice shall be included in
|
188
|
+
all copies or substantial portions of the Software.
|
189
|
+
|
190
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
191
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
192
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
193
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
194
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
195
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
196
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'warden-oauth2'
|
2
|
+
|
3
|
+
module Warden
|
4
|
+
module OAuth2
|
5
|
+
class ErrorApp
|
6
|
+
def self.call(env)
|
7
|
+
new.call(env)
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
warden = env['warden']
|
12
|
+
strategy = warden.winning_strategy
|
13
|
+
status = strategy.respond_to?(:error_status) ? strategy.error_status : 401
|
14
|
+
error_description = strategy.respond_to?(:error_description) ? strategy.error_description : ''
|
15
|
+
headers = {'Content-Type' => 'application/json'}
|
16
|
+
headers['X-Accepted-OAuth-Scopes'] = (strategy.scope || :public).to_s
|
17
|
+
body = %Q{"error":"#{strategy.message}", "error_description":"#{error_description}"}
|
18
|
+
|
19
|
+
Rack::Response.new(body, status, headers).finish
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Warden
|
2
|
+
module OAuth2
|
3
|
+
class FailureApp
|
4
|
+
def self.call(env)
|
5
|
+
new.call(env)
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
warden = env['warden']
|
10
|
+
strategy = warden.winning_strategy
|
11
|
+
|
12
|
+
body = '{"error":"' + strategy.message.to_s + '"}'
|
13
|
+
status = strategy.error_status rescue 401
|
14
|
+
headers = {'Content-Type' => 'application/json'}
|
15
|
+
|
16
|
+
headers['X-Accepted-OAuth-Scopes'] = (strategy.scope || :public).to_s
|
17
|
+
|
18
|
+
[status, headers, [body]]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'warden-oauth2'
|
2
|
+
|
3
|
+
module Warden
|
4
|
+
module OAuth2
|
5
|
+
module Strategies
|
6
|
+
class Bearer < Token
|
7
|
+
def valid?
|
8
|
+
!!token_string
|
9
|
+
end
|
10
|
+
|
11
|
+
def token_string
|
12
|
+
token_string_from_header || token_string_from_request_params
|
13
|
+
end
|
14
|
+
|
15
|
+
def token_string_from_header
|
16
|
+
Rack::Auth::AbstractRequest::AUTHORIZATION_KEYS.each do |key|
|
17
|
+
if env.key?(key) && token_string = env[key][/^Bearer (.*)/, 1]
|
18
|
+
return token_string
|
19
|
+
end
|
20
|
+
end
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def token_string_from_request_params
|
25
|
+
params[:access_token]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'warden-oauth2'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module Warden
|
5
|
+
module OAuth2
|
6
|
+
module Strategies
|
7
|
+
class Client < Base
|
8
|
+
attr_reader :client, :client_id, :client_secret, :error_description
|
9
|
+
|
10
|
+
def authenticate!
|
11
|
+
@client = client_from_http_basic || client_from_request_params
|
12
|
+
|
13
|
+
if self.client
|
14
|
+
fail "invalid_scope" and return if scope && client.respond_to?(:scope) && !client.scope?(scope)
|
15
|
+
client_authenticated
|
16
|
+
else
|
17
|
+
fail "invalid_client"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def client_from_http_basic
|
22
|
+
return nil unless (env.keys & Rack::Auth::AbstractRequest::AUTHORIZATION_KEYS).any?
|
23
|
+
@client_id, @client_secret = *Rack::Auth::Basic::Request.new(env).credentials
|
24
|
+
model.locate(self.client_id, self.client_secret)
|
25
|
+
end
|
26
|
+
|
27
|
+
def client_from_request_params
|
28
|
+
@client_id, @client_secret = params['client_id'], params['client_secret']
|
29
|
+
return nil unless self.client_id
|
30
|
+
model.locate(@client_id, @client_secret)
|
31
|
+
end
|
32
|
+
|
33
|
+
def public_client?
|
34
|
+
client && !client_secret
|
35
|
+
end
|
36
|
+
|
37
|
+
def error_status
|
38
|
+
case message
|
39
|
+
when "invalid_client" then 401
|
40
|
+
when "invalid_scope" then 403
|
41
|
+
else 400
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
attr_writer :error_description
|
47
|
+
def model
|
48
|
+
raise 'Model should be defined in a child strategy'
|
49
|
+
end
|
50
|
+
|
51
|
+
def client_authenticated
|
52
|
+
success! self.client
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'warden-oauth2'
|
2
|
+
|
3
|
+
module Warden
|
4
|
+
module OAuth2
|
5
|
+
module Strategies
|
6
|
+
class ClientCredentials < Client
|
7
|
+
def model
|
8
|
+
Warden::OAuth2.config.client_credentials_model
|
9
|
+
end
|
10
|
+
def valid?
|
11
|
+
params['grant_type'] == 'client_credentials'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'warden-oauth2'
|
2
|
+
|
3
|
+
module Warden
|
4
|
+
module OAuth2
|
5
|
+
module Strategies
|
6
|
+
class Public < Base
|
7
|
+
def authenticate!
|
8
|
+
if scope && scope.to_sym != :public
|
9
|
+
fail! "invalid_scope" and return
|
10
|
+
end
|
11
|
+
|
12
|
+
success! nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def error_status
|
16
|
+
401
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'warden-oauth2'
|
2
|
+
|
3
|
+
module Warden
|
4
|
+
module OAuth2
|
5
|
+
module Strategies
|
6
|
+
class ResourceOwnerPasswordCredentials< Client
|
7
|
+
def valid?
|
8
|
+
params['grant_type'] == 'password'
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
def model
|
13
|
+
Warden::OAuth2.config.resource_owner_password_credentials_model
|
14
|
+
end
|
15
|
+
|
16
|
+
def client_authenticated
|
17
|
+
if params['username'] && params['password']
|
18
|
+
valid_client = client.valid?(username: params['username'], password: params['password'])
|
19
|
+
valid_client ? super : fail("invalid_client")
|
20
|
+
else
|
21
|
+
fail "invalid_request"
|
22
|
+
self.error_description = "username or password are not provided"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'warden-oauth2'
|
2
|
+
|
3
|
+
module Warden
|
4
|
+
module OAuth2
|
5
|
+
module Strategies
|
6
|
+
class Token < Base
|
7
|
+
def valid?
|
8
|
+
!!token_string
|
9
|
+
end
|
10
|
+
|
11
|
+
def authenticate!
|
12
|
+
if token
|
13
|
+
fail! "invalid_token" and return if token.respond_to?(:expired?) && token.expired?
|
14
|
+
fail! "invalid_scope" and return if scope && token.respond_to?(:scope?) && !token.scope?(scope)
|
15
|
+
success! token
|
16
|
+
else
|
17
|
+
fail! "invalid_request" and return unless token
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def token
|
22
|
+
Warden::OAuth2.config.token_model.locate(token_string)
|
23
|
+
end
|
24
|
+
|
25
|
+
def token_string
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
|
29
|
+
def error_status
|
30
|
+
case message
|
31
|
+
when "invalid_token" then 401
|
32
|
+
when "invalid_scope" then 403
|
33
|
+
when "invalid_request" then 400
|
34
|
+
else 400
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'warden'
|
2
|
+
require 'warden/oauth2/version'
|
3
|
+
|
4
|
+
module Warden
|
5
|
+
module OAuth2
|
6
|
+
class Configuration
|
7
|
+
attr_accessor :client_credentials_model, :resource_owner_password_credentials_model, :token_model
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
self.client_credentials_model = ClientCredentialsApplication if defined?(ClientCredentialsApplication)
|
11
|
+
self.resource_owner_password_credentials_model = ResourceOwnerPasswordCredentialsApplication if defined?(ResourceOwnerPasswordCredentialsApplication)
|
12
|
+
self.token_model = AccessToken if defined?(AccessToken)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.config
|
17
|
+
@@config ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.configure
|
21
|
+
yield config
|
22
|
+
end
|
23
|
+
|
24
|
+
autoload :FailureApp, 'warden/oauth2/failure_app'
|
25
|
+
module Strategies
|
26
|
+
autoload :Base, 'warden/oauth2/strategies/base'
|
27
|
+
autoload :Public, 'warden/oauth2/strategies/public'
|
28
|
+
autoload :Token, 'warden/oauth2/strategies/token'
|
29
|
+
autoload :Client, 'warden/oauth2/strategies/client'
|
30
|
+
autoload :ClientCredentials, 'warden/oauth2/strategies/client_credentials'
|
31
|
+
autoload :ResourceOwnerPasswordCredentials, 'warden/oauth2/strategies/resource_owner_password_credentials'
|
32
|
+
autoload :Bearer, 'warden/oauth2/strategies/bearer'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'warden/oauth2'
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Warden::OAuth2::FailureApp do
|
4
|
+
let(:app){ subject }
|
5
|
+
let(:warden){ double(:winning_strategy => @strategy || strategy) }
|
6
|
+
let(:strategy){ double(:message => 'invalid_request') }
|
7
|
+
|
8
|
+
context 'with all info' do
|
9
|
+
before do
|
10
|
+
@strategy = double(:error_status => 502, :message => 'custom', :scope => 'random')
|
11
|
+
get '/unauthenticated', {}, 'warden' => warden
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should set the status from error_status if there is one' do
|
15
|
+
last_response.status.should == 502
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should set the message from the message' do
|
19
|
+
last_response.body.should == '{"error":"custom"}'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should set the content type' do
|
23
|
+
last_response.headers['Content-Type'].should == 'application/json'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should set the X-OAuth-Accepted-Scopes header' do
|
27
|
+
last_response.headers['X-Accepted-OAuth-Scopes'].should == 'random'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Warden::OAuth2::Strategies::Bearer do
|
4
|
+
let(:strategy){ Warden::OAuth2::Strategies::Bearer }
|
5
|
+
let(:token_model){ double(:AccessToken) }
|
6
|
+
subject{ strategy.new({'rack.input' => {}}) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
Warden::OAuth2.config.token_model = token_model
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#token_string_from_header' do
|
13
|
+
Rack::Auth::AbstractRequest::AUTHORIZATION_KEYS.each do |key|
|
14
|
+
it "should recognize a bearer token in the #{key} environment key" do
|
15
|
+
subject.stub(:env).and_return({key => "Bearer abc"})
|
16
|
+
subject.token_string_from_header.should == 'abc'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should ignore a non-bearer authorization header' do
|
21
|
+
subject.stub(:env).and_return('HTTP_AUTHORIZATION' => 'Other do do do')
|
22
|
+
subject.token_string_from_header.should be_nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#token_string_from_request_params' do
|
27
|
+
it 'should pull the :access_token param' do
|
28
|
+
subject.stub(:params).and_return(:access_token => 'abc')
|
29
|
+
subject.token_string_from_request_params.should == 'abc'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Warden::OAuth2::Strategies::ClientCredentials do
|
4
|
+
let(:strategy){ described_class }
|
5
|
+
let(:client_credentials_model){ double(:ClientApplication) }
|
6
|
+
subject{ strategy.new({'rack.input' => {}}) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
Warden::OAuth2.config.client_credentials_model = client_credentials_model
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#valid?' do
|
13
|
+
it 'returns false if the grant type is not specified' do
|
14
|
+
subject.stub(:params).and_return({})
|
15
|
+
subject.should_not be_valid
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns true if the grant type is client_credentials' do
|
19
|
+
subject.stub(:params).and_return({'grant_type' => 'client_credentials'})
|
20
|
+
subject.should be_valid
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns false if the grant type is not client_credentials' do
|
24
|
+
subject.stub(:params).and_return({'grant_type' => 'whatever'})
|
25
|
+
subject.should_not be_valid
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Warden::OAuth2::Strategies::Client do
|
4
|
+
let(:strategy){ Warden::OAuth2::Strategies::Client }
|
5
|
+
let(:client_model){ double(:ClientApplication) }
|
6
|
+
subject{ strategy.new({'rack.input' => {}}) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
subject.stub(:model).and_return(client_model)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#client_from_http_basic' do
|
13
|
+
it 'should call through to the client application class locate method' do
|
14
|
+
subject.stub(:env).and_return({
|
15
|
+
'HTTP_AUTHORIZATION' => "Basic #{Base64.encode64('id:secret')}"
|
16
|
+
})
|
17
|
+
|
18
|
+
client_model.should_receive(:locate).with('id','secret').and_return("booya")
|
19
|
+
subject.client_from_http_basic.should == "booya"
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should return nil if no HTTP Basic credentials are provided' do
|
23
|
+
subject.client_from_http_basic.should be_nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#client_from_request_params' do
|
28
|
+
it 'should be nil if no client_id is provided' do
|
29
|
+
subject.stub(:params).and_return({'client_secret' => 'abc'})
|
30
|
+
subject.client_from_request_params.should be_nil
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should call through to locate if a client_id is present' do
|
34
|
+
subject.stub(:params).and_return({'client_id' => 'abc'})
|
35
|
+
client_model.should_receive(:locate).with('abc',nil)
|
36
|
+
subject.client_from_request_params
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should call through to locate if a client_id and secret are present' do
|
40
|
+
subject.stub(:params).and_return({'client_id' => 'abc', 'client_secret' => 'def'})
|
41
|
+
client_model.should_receive(:locate).with('abc','def')
|
42
|
+
subject.client_from_request_params
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#authorize!' do
|
47
|
+
it 'should succeed if a client is around' do
|
48
|
+
client_instance = double
|
49
|
+
client_model.stub(:locate).and_return(client_instance)
|
50
|
+
subject.stub(:params).and_return('client_id' => 'awesome')
|
51
|
+
subject._run!
|
52
|
+
subject.user.should == client_instance
|
53
|
+
subject.result.should == :success
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should fail if no credentials are passed' do
|
57
|
+
subject._run!
|
58
|
+
subject.result.should == :failure
|
59
|
+
subject.message.should == "invalid_client"
|
60
|
+
subject.error_status.should == 401
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should fail if insufficient scope is provided' do
|
64
|
+
client_model.stub(:locate).and_return(double(:respond_to? => true, :scope? => false))
|
65
|
+
subject.stub(:params).and_return('client_id' => 'abc')
|
66
|
+
subject.stub(:scope).and_return(:confidential_client)
|
67
|
+
subject._run!
|
68
|
+
subject.result.should == :failure
|
69
|
+
subject.message.should == "invalid_scope"
|
70
|
+
subject.error_status.should == 403
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Warden::OAuth2::Strategies::Public do
|
4
|
+
let(:env){ {'PATH_INFO' => '/resource'} }
|
5
|
+
let(:strategy){ Warden::OAuth2::Strategies::Public }
|
6
|
+
subject{ strategy.new(env) }
|
7
|
+
|
8
|
+
it 'should succeed with no scope' do
|
9
|
+
subject._run!
|
10
|
+
subject.result.should == :success
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should succeed with a :public scope' do
|
14
|
+
subject.stub(:scope).and_return(:public)
|
15
|
+
subject._run!
|
16
|
+
subject.result.should == :success
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should fail and halt with another scope' do
|
20
|
+
subject.stub(:scope).and_return(:user)
|
21
|
+
subject._run!
|
22
|
+
subject.should be_halted
|
23
|
+
subject.message.should == "invalid_scope"
|
24
|
+
subject.result.should == :failure
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Warden::OAuth2::Strategies::ResourceOwnerPasswordCredentials do
|
4
|
+
let(:strategy){ described_class }
|
5
|
+
let(:client_model){ double(:ClientApplication) }
|
6
|
+
subject{ strategy.new({'rack.input' => {}}) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
Warden::OAuth2.config.resource_owner_password_credentials_model = client_model
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#valid?' do
|
13
|
+
it 'returns false if the grant type is not specified' do
|
14
|
+
subject.stub(:params).and_return({})
|
15
|
+
subject.should_not be_valid
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns true if the grant type is password' do
|
19
|
+
subject.stub(:params).and_return({'grant_type' => 'password'})
|
20
|
+
subject.should be_valid
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns false if the grant type is not password' do
|
24
|
+
subject.stub(:params).and_return({'grant_type' => 'whatever'})
|
25
|
+
subject.should_not be_valid
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#authorize!' do
|
30
|
+
it 'should fail if a client is around but not valid' do
|
31
|
+
client_instance = double(:client_instance, valid?: false)
|
32
|
+
client_model.stub(locate: client_instance)
|
33
|
+
subject.stub(:params).and_return('client_id' => 'awesome', 'username' => 'someuser', 'password' => 'incorrect')
|
34
|
+
subject._run!
|
35
|
+
subject.message.should == "invalid_client"
|
36
|
+
subject.error_status.should == 401
|
37
|
+
end
|
38
|
+
it 'should fail if username and password are not provided' do
|
39
|
+
client_model.stub(locate: double)
|
40
|
+
subject.stub(:params).and_return('client_id' => 'awesome')
|
41
|
+
subject._run!
|
42
|
+
subject.message.should == "invalid_request"
|
43
|
+
subject.error_status.should == 400
|
44
|
+
subject.error_description.should_not be_empty
|
45
|
+
end
|
46
|
+
it 'should pass username and password to validation check' do
|
47
|
+
client_instance = double(:client_instance)
|
48
|
+
client_model.stub(locate: client_instance)
|
49
|
+
subject.stub(:params).and_return('client_id' => 'awesome', 'username' => 'username', 'password' => 'password')
|
50
|
+
|
51
|
+
client_instance.should_receive(:valid?).with(username: 'username', password: 'password').and_return(false)
|
52
|
+
|
53
|
+
subject._run!
|
54
|
+
end
|
55
|
+
it 'should succeed if a client is around and valid' do
|
56
|
+
client_instance = double(:client_instance, valid?: true)
|
57
|
+
client_model.stub(locate: client_instance)
|
58
|
+
subject.stub(:params).and_return('client_id' => 'awesome', 'username' => 'username', 'password' => 'correct')
|
59
|
+
subject._run!
|
60
|
+
subject.user.should == client_instance
|
61
|
+
subject.result.should == :success
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Warden::OAuth2::Strategies::Token do
|
4
|
+
let(:token_model){ double }
|
5
|
+
let(:strategy){ Warden::OAuth2::Strategies::Token }
|
6
|
+
subject{ strategy.new({'rack.input' => {}}) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
Warden::OAuth2.config.token_model = token_model
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#token' do
|
13
|
+
it 'should call through to .locate on the token_class with the token string' do
|
14
|
+
token_model.should_receive(:locate).with('abc')
|
15
|
+
subject.stub(:token_string).and_return('abc')
|
16
|
+
subject.token
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#authenticate!' do
|
21
|
+
it 'should be successful if there is a token' do
|
22
|
+
token_instance = double
|
23
|
+
subject.stub(:token).and_return(token_instance)
|
24
|
+
subject._run!
|
25
|
+
subject.result.should == :success
|
26
|
+
subject.user.should == token_instance
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should fail if there is not a token' do
|
30
|
+
subject.stub(token: nil)
|
31
|
+
subject._run!
|
32
|
+
subject.result.should == :failure
|
33
|
+
subject.message.should == "invalid_request"
|
34
|
+
subject.error_status.should == 400
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should fail if the access token is expired' do
|
38
|
+
token_instance = double(:respond_to? => true, :expired? => true, :scope? => true)
|
39
|
+
subject.stub(:token).and_return(token_instance)
|
40
|
+
subject._run!
|
41
|
+
subject.result.should == :failure
|
42
|
+
subject.message.should == "invalid_token"
|
43
|
+
subject.error_status.should == 401
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should fail if there is insufficient scope' do
|
47
|
+
token_instance = double(:respond_to? => true, :expired? => false, :scope? => false)
|
48
|
+
subject.stub(:token).and_return(token_instance)
|
49
|
+
subject.stub(:scope).and_return(:secret)
|
50
|
+
subject._run!
|
51
|
+
subject.result.should == :failure
|
52
|
+
subject.message.should == "invalid_scope"
|
53
|
+
subject.error_status.should == 403
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/warden/oauth2/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["AirService"]
|
6
|
+
gem.email = ["devs@airservice.co"]
|
7
|
+
gem.description = %q{OAuth 2.0 strategies for Warden}
|
8
|
+
gem.summary = %q{OAuth 2.0 strategies for Warden}
|
9
|
+
gem.homepage = "https://github.com/airservice/warden-oauth2"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "warden-oauth2-strategies"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Warden::OAuth2::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'warden'
|
19
|
+
gem.add_development_dependency 'rake'
|
20
|
+
gem.add_development_dependency 'rspec'
|
21
|
+
gem.add_development_dependency 'rack-test'
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: warden-oauth2-strategies
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- AirService
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: warden
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rack-test
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: OAuth 2.0 strategies for Warden
|
70
|
+
email:
|
71
|
+
- devs@airservice.co
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- .rspec
|
78
|
+
- .travis.yml
|
79
|
+
- Gemfile
|
80
|
+
- Guardfile
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- lib/warden-oauth2.rb
|
84
|
+
- lib/warden/oauth2.rb
|
85
|
+
- lib/warden/oauth2/error_app.rb
|
86
|
+
- lib/warden/oauth2/failure_app.rb
|
87
|
+
- lib/warden/oauth2/strategies/base.rb
|
88
|
+
- lib/warden/oauth2/strategies/bearer.rb
|
89
|
+
- lib/warden/oauth2/strategies/client.rb
|
90
|
+
- lib/warden/oauth2/strategies/client_credentials.rb
|
91
|
+
- lib/warden/oauth2/strategies/public.rb
|
92
|
+
- lib/warden/oauth2/strategies/resource_owner_password_credentials.rb
|
93
|
+
- lib/warden/oauth2/strategies/token.rb
|
94
|
+
- lib/warden/oauth2/version.rb
|
95
|
+
- spec/spec_helper.rb
|
96
|
+
- spec/warden/oauth2/failure_app_spec.rb
|
97
|
+
- spec/warden/oauth2/strategies/bearer_spec.rb
|
98
|
+
- spec/warden/oauth2/strategies/client_credentials_spec.rb
|
99
|
+
- spec/warden/oauth2/strategies/client_spec.rb
|
100
|
+
- spec/warden/oauth2/strategies/public_spec.rb
|
101
|
+
- spec/warden/oauth2/strategies/resource_owner_password_credentials_spec.rb
|
102
|
+
- spec/warden/oauth2/strategies/token_spec.rb
|
103
|
+
- warden-oauth2.gemspec
|
104
|
+
homepage: https://github.com/airservice/warden-oauth2
|
105
|
+
licenses: []
|
106
|
+
metadata: {}
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 2.2.1
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: OAuth 2.0 strategies for Warden
|
127
|
+
test_files:
|
128
|
+
- spec/spec_helper.rb
|
129
|
+
- spec/warden/oauth2/failure_app_spec.rb
|
130
|
+
- spec/warden/oauth2/strategies/bearer_spec.rb
|
131
|
+
- spec/warden/oauth2/strategies/client_credentials_spec.rb
|
132
|
+
- spec/warden/oauth2/strategies/client_spec.rb
|
133
|
+
- spec/warden/oauth2/strategies/public_spec.rb
|
134
|
+
- spec/warden/oauth2/strategies/resource_owner_password_credentials_spec.rb
|
135
|
+
- spec/warden/oauth2/strategies/token_spec.rb
|