oauth2-rack 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/Gemfile +7 -0
- data/Guardfile +10 -0
- data/Rakefile +4 -0
- data/config.ru +50 -0
- data/examples/ruby-client/password_authorization.rb +6 -0
- data/lib/oauth2/rack/authentication/client/http_basic.rb +61 -0
- data/lib/oauth2/rack/authentication/client/request_params.rb +55 -0
- data/lib/oauth2/rack/authentication/client.rb +5 -0
- data/lib/oauth2/rack/authentication/resource_owner/request_params.rb +54 -0
- data/lib/oauth2/rack/authentication/resource_owner.rb +4 -0
- data/lib/oauth2/rack/authentication.rb +6 -0
- data/lib/oauth2/rack/authorization/client_credentials/access_token_issuer.rb +58 -0
- data/lib/oauth2/rack/authorization/client_credentials.rb +5 -0
- data/lib/oauth2/rack/authorization/password/access_token_issuer.rb +60 -0
- data/lib/oauth2/rack/authorization/password.rb +5 -0
- data/lib/oauth2/rack/authorization.rb +6 -0
- data/lib/oauth2/rack.rb +9 -0
- data/lib/oauth2-rack.rb +1 -0
- data/spec/oauth2/rack/authentication/client/http_basic_spec.rb +76 -0
- data/spec/oauth2/rack/authentication/client/request_params_spec.rb +86 -0
- data/spec/oauth2/rack/authentication/resource_owner/request_params_spec.rb +86 -0
- data/spec/oauth2/rack/authorization/client_credentials/access_token_issuer_spec.rb +99 -0
- data/spec/oauth2/rack/authorization/password/access_token_issuer_spec.rb +101 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/rake_test_helper.rb +97 -0
- metadata +167 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'rspec', :version => 2 do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec/" }
|
8
|
+
watch('spec/spec_helper.rb') { "spec/" }
|
9
|
+
end
|
10
|
+
|
data/Rakefile
ADDED
data/config.ru
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
$: << File.expand_path('../lib', __FILE__)
|
2
|
+
require 'oauth2/rack'
|
3
|
+
|
4
|
+
class AccessTokenIssuer
|
5
|
+
def self.call(opts = {})
|
6
|
+
opts.merge('access_token' => 'test')
|
7
|
+
end
|
8
|
+
end
|
9
|
+
class Authenticator
|
10
|
+
def self.call(opts = {})
|
11
|
+
if opts[:username]
|
12
|
+
authenticate_resource_owner(opts)
|
13
|
+
elsif opts[:client_id]
|
14
|
+
authenticate_client(opts)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.authenticate_resource_owner(opts)
|
19
|
+
OpenStruct.new(:username => opts[:username])
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.authenticate_client(opts)
|
23
|
+
OpenStruct.new(:client_id => opts[:client_id])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
map '/password/access_token' do
|
28
|
+
use OAuth2::Rack::Authentication::Client::HTTPBasic, :required => false, :authenticator => Authenticator
|
29
|
+
use OAuth2::Rack::Authentication::Client::RequestParams, :required => false, :authenticator => Authenticator
|
30
|
+
|
31
|
+
use OAuth2::Rack::Authentication::ResourceOwner::RequestParams, :authenticator => Authenticator
|
32
|
+
|
33
|
+
use OAuth2::Rack::Authorization::Password::AccessTokenIssuer, :issuer => AccessTokenIssuer
|
34
|
+
|
35
|
+
run proc { |env| [200, {'Content-Type' => 'text/html'}, ['Hello']] }
|
36
|
+
end
|
37
|
+
|
38
|
+
map '/client_credentials/access_token' do
|
39
|
+
use OAuth2::Rack::Authentication::Client::HTTPBasic, :required => false, :authenticator => Authenticator
|
40
|
+
use OAuth2::Rack::Authentication::Client::RequestParams, :required => false, :authenticator => Authenticator
|
41
|
+
|
42
|
+
use OAuth2::Rack::Authorization::ClientCredentials::AccessTokenIssuer, :issuer => AccessTokenIssuer
|
43
|
+
|
44
|
+
run proc { |env| [200, {'Content-Type' => 'text/html'}, ['Hello']] }
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
map '/inspect' do
|
49
|
+
run proc { |env| [200, {'Content-Type' => 'text/html'}, [env.inspect]] }
|
50
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'oauth2/rack'
|
2
|
+
|
3
|
+
# 2.4.1. Client Password
|
4
|
+
class OAuth2::Rack::Authentication::Client::HTTPBasic
|
5
|
+
HEADER_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION']
|
6
|
+
|
7
|
+
def initialize(app, opts = {}, &authenticator)
|
8
|
+
@app = app
|
9
|
+
@realm = opts.delete(:realm)
|
10
|
+
@required = opts.fetch(:required, true)
|
11
|
+
opts.delete(:required)
|
12
|
+
@authenticator = authenticator || opts.delete(:authenticator)
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
return @app.call(env) if env.has_key?('oauth2.client')
|
17
|
+
|
18
|
+
key = HEADER_KEYS.find { |k| env.has_key?(k) }
|
19
|
+
auth_string = env[key]
|
20
|
+
|
21
|
+
if auth_string.nil?
|
22
|
+
return @required ? unauthorized : @app.call(env)
|
23
|
+
end
|
24
|
+
|
25
|
+
schema, credentials = auth_string.split(' ', 2)
|
26
|
+
if schema.downcase != 'basic'
|
27
|
+
return bad_request
|
28
|
+
end
|
29
|
+
|
30
|
+
client_id, client_secret = credentials.unpack('m*').first.split(':', 2)
|
31
|
+
client = @authenticator.call(:client_id => client_id, :client_secret => client_secret)
|
32
|
+
if client
|
33
|
+
env['oauth2.client'] = client
|
34
|
+
@app.call(env)
|
35
|
+
else
|
36
|
+
unauthorized
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def authenticate(opts)
|
42
|
+
@authenticator && @authenticator.call(opts)
|
43
|
+
end
|
44
|
+
|
45
|
+
def unauthorized
|
46
|
+
[ 401,
|
47
|
+
{ 'Content-Type' => 'text/plain',
|
48
|
+
'Content-Length' => '0',
|
49
|
+
'WWW-Authenticate' => 'Basic realm="%s"' % @realm },
|
50
|
+
[]
|
51
|
+
]
|
52
|
+
end
|
53
|
+
|
54
|
+
def bad_request
|
55
|
+
[ 400,
|
56
|
+
{ 'Content-Type' => 'text/plain',
|
57
|
+
'Content-Length' => '0' },
|
58
|
+
[]
|
59
|
+
]
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'oauth2/rack'
|
2
|
+
|
3
|
+
# 2.4.1. Client Password
|
4
|
+
# Send client_id and client_secret in request params
|
5
|
+
class OAuth2::Rack::Authentication::Client::RequestParams
|
6
|
+
def initialize(app, opts = {}, &authenticator)
|
7
|
+
@app = app
|
8
|
+
@required = opts.fetch(:required, true)
|
9
|
+
opts.delete(:required)
|
10
|
+
@authenticator = authenticator || opts.delete(:authenticator)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
return @app.call(env) if env.has_key?('oauth2.client')
|
15
|
+
|
16
|
+
@request = Rack::Request.new(env)
|
17
|
+
|
18
|
+
client_id = @request['client_id']
|
19
|
+
client_secret = @request['client_secret']
|
20
|
+
if client_id.nil? && client_secret.nil?
|
21
|
+
return @required ? unauthorized : @app.call(env)
|
22
|
+
elsif client_id.nil? || client_secret.nil?
|
23
|
+
return bad_request
|
24
|
+
end
|
25
|
+
|
26
|
+
client = @authenticator.call(:client_id => client_id, :client_secret => client_secret)
|
27
|
+
if client
|
28
|
+
env['oauth2.client'] = client
|
29
|
+
@app.call(env)
|
30
|
+
else
|
31
|
+
unauthorized
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def authenticate(opts)
|
37
|
+
@authenticator && @authenticator.call(opts)
|
38
|
+
end
|
39
|
+
|
40
|
+
def unauthorized
|
41
|
+
[ 401,
|
42
|
+
{ 'Content-Type' => 'text/plain',
|
43
|
+
'Content-Length' => '0' },
|
44
|
+
[]
|
45
|
+
]
|
46
|
+
end
|
47
|
+
|
48
|
+
def bad_request
|
49
|
+
[ 400,
|
50
|
+
{ 'Content-Type' => 'text/plain',
|
51
|
+
'Content-Length' => '0' },
|
52
|
+
[]
|
53
|
+
]
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'oauth2/rack'
|
2
|
+
|
3
|
+
# Authenticate resource owner with request params
|
4
|
+
class OAuth2::Rack::Authentication::ResourceOwner::RequestParams
|
5
|
+
def initialize(app, opts = {}, &authenticator)
|
6
|
+
@app = app
|
7
|
+
@required = opts.fetch(:required, true)
|
8
|
+
opts.delete(:required)
|
9
|
+
@authenticator = authenticator || opts.delete(:authenticator)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
return @app.call(env) if env.has_key?('oauth2.resource_owner')
|
14
|
+
|
15
|
+
@request = Rack::Request.new(env)
|
16
|
+
|
17
|
+
username = @request['username']
|
18
|
+
password = @request['password']
|
19
|
+
if username.nil? && password.nil?
|
20
|
+
return @required ? unauthorized : @app.call(env)
|
21
|
+
elsif username.nil? || password.nil?
|
22
|
+
return bad_request
|
23
|
+
end
|
24
|
+
|
25
|
+
resource_owner = @authenticator.call(:username => username, :password => password)
|
26
|
+
if resource_owner
|
27
|
+
env['oauth2.resource_owner'] = resource_owner
|
28
|
+
@app.call(env)
|
29
|
+
else
|
30
|
+
unauthorized
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def authenticate(opts)
|
36
|
+
@authenticator && @authenticator.call(opts)
|
37
|
+
end
|
38
|
+
|
39
|
+
def unauthorized
|
40
|
+
[ 401,
|
41
|
+
{ 'Content-Type' => 'text/plain',
|
42
|
+
'Content-Length' => '0' },
|
43
|
+
[]
|
44
|
+
]
|
45
|
+
end
|
46
|
+
|
47
|
+
def bad_request
|
48
|
+
[ 400,
|
49
|
+
{ 'Content-Type' => 'text/plain',
|
50
|
+
'Content-Length' => '0' },
|
51
|
+
[]
|
52
|
+
]
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# @see 4.4.1 Client Credentials
|
2
|
+
require 'oauth2/rack'
|
3
|
+
require 'multi_json'
|
4
|
+
|
5
|
+
class OAuth2::Rack::Authorization::ClientCredentials::AccessTokenIssuer
|
6
|
+
def initialize(app, opts = {}, &issuer)
|
7
|
+
@app = app
|
8
|
+
|
9
|
+
@issuer = issuer || opts.delete(:issuer)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
client = env['oauth2.client']
|
14
|
+
unless client
|
15
|
+
return error_response(:error => 'invalid_client')
|
16
|
+
end
|
17
|
+
|
18
|
+
request = Rack::Request.new(env)
|
19
|
+
unless request['grant_type'] == 'client_credentials'
|
20
|
+
return error_response(:error => 'invalid_request')
|
21
|
+
end
|
22
|
+
|
23
|
+
access_token = find_acccess_token(:grant_type => 'client_credentials',
|
24
|
+
:client => client,
|
25
|
+
:scope => request['scope'])
|
26
|
+
|
27
|
+
if access_token['error']
|
28
|
+
error_response(access_token)
|
29
|
+
else
|
30
|
+
successful_response(access_token)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def find_acccess_token(opts)
|
36
|
+
if @issuer
|
37
|
+
@issuer.call(opts)
|
38
|
+
end || { 'error' => 'unauthorized_client' }
|
39
|
+
end
|
40
|
+
|
41
|
+
def successful_response(response_object)
|
42
|
+
headers = {
|
43
|
+
'Content-Type' => 'application/json;charset=UTF-8',
|
44
|
+
'Cache-Control' => 'no-store',
|
45
|
+
'Pragma' => 'no-cache'
|
46
|
+
}
|
47
|
+
|
48
|
+
[200, headers, [MultiJson.encode(response_object)]]
|
49
|
+
end
|
50
|
+
|
51
|
+
def error_response(response_object)
|
52
|
+
headers = {
|
53
|
+
'Content-Type' => 'application/json;charset=UTF-8'
|
54
|
+
}
|
55
|
+
|
56
|
+
[400, headers, [MultiJson.encode(response_object)]]
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# @see 4.3. Resource Owner Password Credentials
|
2
|
+
require 'oauth2/rack'
|
3
|
+
require 'multi_json'
|
4
|
+
|
5
|
+
class OAuth2::Rack::Authorization::Password::AccessTokenIssuer
|
6
|
+
def initialize(app, opts = {}, &issuer)
|
7
|
+
@app = app
|
8
|
+
|
9
|
+
@issuer = issuer || opts.delete(:issuer)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
resource_owner = env['oauth2.resource_owner']
|
14
|
+
unless resource_owner
|
15
|
+
return error_response(:error => 'invalid_grant')
|
16
|
+
end
|
17
|
+
|
18
|
+
request = Rack::Request.new(env)
|
19
|
+
unless request['grant_type'] == 'password'
|
20
|
+
return error_response(:error => 'invalid_request')
|
21
|
+
end
|
22
|
+
|
23
|
+
# oauth2.client is set in client authentication
|
24
|
+
access_token = find_acccess_token(:grant_type => 'password',
|
25
|
+
:resource_owner => resource_owner,
|
26
|
+
:client => env['oath2.client'],
|
27
|
+
:scope => request['scope'])
|
28
|
+
|
29
|
+
if access_token['error']
|
30
|
+
error_response(access_token)
|
31
|
+
else
|
32
|
+
successful_response(access_token)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def find_acccess_token(opts)
|
38
|
+
if @issuer
|
39
|
+
@issuer.call(opts)
|
40
|
+
end || { 'error' => 'unauthorized_client' }
|
41
|
+
end
|
42
|
+
|
43
|
+
def successful_response(response_object)
|
44
|
+
headers = {
|
45
|
+
'Content-Type' => 'application/json;charset=UTF-8',
|
46
|
+
'Cache-Control' => 'no-store',
|
47
|
+
'Pragma' => 'no-cache'
|
48
|
+
}
|
49
|
+
|
50
|
+
[200, headers, [MultiJson.encode(response_object)]]
|
51
|
+
end
|
52
|
+
|
53
|
+
def error_response(response_object)
|
54
|
+
headers = {
|
55
|
+
'Content-Type' => 'application/json;charset=UTF-8'
|
56
|
+
}
|
57
|
+
|
58
|
+
[400, headers, [MultiJson.encode(response_object)]]
|
59
|
+
end
|
60
|
+
end
|
data/lib/oauth2/rack.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'oauth2/rack/version'
|
2
|
+
|
3
|
+
module OAuth2
|
4
|
+
# Oauth2::Rack module, the top namespace for all oauth2-rack modules and classes
|
5
|
+
module Rack
|
6
|
+
autoload :Authorization, 'oauth2/rack/authorization'
|
7
|
+
autoload :Authentication, 'oauth2/rack/authentication'
|
8
|
+
end
|
9
|
+
end
|
data/lib/oauth2-rack.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'oauth2/rack'
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::Rack::Authentication::Client::HTTPBasic do
|
4
|
+
let(:client) { double('client').as_null_object }
|
5
|
+
let(:authenticator) { double('authenticator').as_null_object }
|
6
|
+
let(:opts) { Hash.new }
|
7
|
+
let(:chained_app_response) { [200, { 'Content-Type' => 'text/plain' }, []] }
|
8
|
+
|
9
|
+
def do_request
|
10
|
+
post '/', opts
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when oauth2.client is already set' do
|
14
|
+
it 'continues the app' do
|
15
|
+
opts['oauth2.client'] = client
|
16
|
+
chained_app.should_receive(:call).with(hash_including('oauth2.client' => client)).and_return(chained_app_response)
|
17
|
+
do_request
|
18
|
+
response.status.should eq(200)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when auth header is not specified' do
|
23
|
+
context 'and http basic auth is required' do
|
24
|
+
it 'responds with 401 unauthorized' do
|
25
|
+
do_request
|
26
|
+
response.status.should eq(401)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
context 'and http basic auth is optional' do
|
30
|
+
app { OAuth2::Rack::Authentication::Client::HTTPBasic.new(chained_app, :required => false) }
|
31
|
+
it 'continues the app' do
|
32
|
+
chained_app.should_receive(:call).with(hash_not_including('oauth2.client')).and_return(chained_app_response)
|
33
|
+
do_request
|
34
|
+
response.status.should eq(200)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when schema is not basic' do
|
40
|
+
before { opts['HTTP_AUTHORIZATION'] = 'Digest xxx' }
|
41
|
+
|
42
|
+
it 'responds with 400 bad request' do
|
43
|
+
do_request
|
44
|
+
response.status.should eq(400)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'when schema is basic' do
|
49
|
+
app {
|
50
|
+
OAuth2::Rack::Authentication::Client::HTTPBasic.new(chained_app) do |opts|
|
51
|
+
authenticator.call(opts)
|
52
|
+
end
|
53
|
+
}
|
54
|
+
before { opts['HTTP_AUTHORIZATION'] = "Basic #{["user:pass"].pack('m*')}" }
|
55
|
+
|
56
|
+
context 'and credentials are valid' do
|
57
|
+
it 'sets oauth2.client in env' do
|
58
|
+
authenticator.should_receive(:call).with(:client_id => 'user', :client_secret => 'pass').and_return(client)
|
59
|
+
chained_app.should_receive(:call).with(hash_including('oauth2.client' => client)).and_return(chained_app_response)
|
60
|
+
|
61
|
+
do_request
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'but credentials are invalid' do
|
66
|
+
it 'responds with 401 unauthorized' do
|
67
|
+
authenticator.should_receive(:call).with(:client_id => 'user', :client_secret => 'pass').and_return(nil)
|
68
|
+
chained_app.should_not_receive(:call)
|
69
|
+
|
70
|
+
do_request
|
71
|
+
|
72
|
+
response.status.should eq(401)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::Rack::Authentication::Client::RequestParams do
|
4
|
+
let(:client) { double('client').as_null_object }
|
5
|
+
let(:authenticator) { double('authenticator').as_null_object }
|
6
|
+
let(:params) { Hash.new }
|
7
|
+
let(:opts) { Hash[:params => params] }
|
8
|
+
let(:chained_app_response) { [200, { 'Content-Type' => 'text/plain' }, []] }
|
9
|
+
|
10
|
+
def do_request
|
11
|
+
post '/', opts
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'when oauth2.client is already set' do
|
15
|
+
it 'continues the app' do
|
16
|
+
opts['oauth2.client'] = client
|
17
|
+
chained_app.should_receive(:call).with(hash_including('oauth2.client' => client)).and_return(chained_app_response)
|
18
|
+
do_request
|
19
|
+
response.status.should eq(200)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when client_id and client_secret are both missed' do
|
24
|
+
context 'and request params auth is required' do
|
25
|
+
it 'responds with 401 unauthorized' do
|
26
|
+
do_request
|
27
|
+
response.status.should eq(401)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
context 'and request params auth is optional' do
|
31
|
+
app { OAuth2::Rack::Authentication::Client::RequestParams.new(chained_app, :required => false) }
|
32
|
+
it 'continues the app' do
|
33
|
+
chained_app.should_receive(:call).with(hash_not_including('oauth2.client')).and_return(chained_app_response)
|
34
|
+
do_request
|
35
|
+
response.status.should eq(200)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when client_id is missed' do
|
41
|
+
before { params['client_secret'] = 'secret' }
|
42
|
+
|
43
|
+
it 'responds with 400 bad request' do
|
44
|
+
do_request
|
45
|
+
response.status.should eq(400)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
context 'when client_secret is missed' do
|
49
|
+
before { params['client_id'] = 'client_x' }
|
50
|
+
|
51
|
+
it 'responds with 400 bad request' do
|
52
|
+
do_request
|
53
|
+
response.status.should eq(400)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when client_id and client_secret are specified' do
|
58
|
+
app {
|
59
|
+
OAuth2::Rack::Authentication::Client::RequestParams.new(chained_app) do |opts|
|
60
|
+
authenticator.call(opts)
|
61
|
+
end
|
62
|
+
}
|
63
|
+
let(:credentials) { Hash[:client_id => 'client_x', :client_secret => 'secret'] }
|
64
|
+
before { params.merge! credentials }
|
65
|
+
|
66
|
+
context 'and credentials are valid' do
|
67
|
+
it 'sets oauth2.client in env' do
|
68
|
+
authenticator.should_receive(:call).with(credentials).and_return(client)
|
69
|
+
chained_app.should_receive(:call).with(hash_including('oauth2.client' => client)).and_return(chained_app_response)
|
70
|
+
|
71
|
+
do_request
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'but credentials are invalid' do
|
76
|
+
it 'responds with 401 unauthorized' do
|
77
|
+
authenticator.should_receive(:call).with(credentials).and_return(nil)
|
78
|
+
chained_app.should_not_receive(:call)
|
79
|
+
|
80
|
+
do_request
|
81
|
+
|
82
|
+
response.status.should eq(401)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::Rack::Authentication::ResourceOwner::RequestParams do
|
4
|
+
let(:resource_owner) { double('resource_owner').as_null_object }
|
5
|
+
let(:authenticator) { double('authenticator').as_null_object }
|
6
|
+
let(:params) { Hash.new }
|
7
|
+
let(:opts) { Hash[:params => params] }
|
8
|
+
let(:chained_app_response) { [200, { 'Content-Type' => 'text/plain' }, []] }
|
9
|
+
|
10
|
+
def do_request
|
11
|
+
post '/', opts
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'when oauth2.resource_owner is already set' do
|
15
|
+
it 'continues the app' do
|
16
|
+
opts['oauth2.resource_owner'] = resource_owner
|
17
|
+
chained_app.should_receive(:call).with(hash_including('oauth2.resource_owner' => resource_owner)).and_return(chained_app_response)
|
18
|
+
do_request
|
19
|
+
response.status.should eq(200)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when username and password are both missed' do
|
24
|
+
context 'and request params auth is required' do
|
25
|
+
it 'responds with 401 unauthorized' do
|
26
|
+
do_request
|
27
|
+
response.status.should eq(401)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
context 'and request params auth is optional' do
|
31
|
+
app { OAuth2::Rack::Authentication::ResourceOwner::RequestParams.new(chained_app, :required => false) }
|
32
|
+
it 'continues the app' do
|
33
|
+
chained_app.should_receive(:call).with(hash_not_including('oauth2.resource_owner')).and_return(chained_app_response)
|
34
|
+
do_request
|
35
|
+
response.status.should eq(200)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when username is missed' do
|
41
|
+
before { params['password'] = 'secret' }
|
42
|
+
|
43
|
+
it 'responds with 400 bad request' do
|
44
|
+
do_request
|
45
|
+
response.status.should eq(400)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
context 'when password is missed' do
|
49
|
+
before { params['username'] = 'user_x' }
|
50
|
+
|
51
|
+
it 'responds with 400 bad request' do
|
52
|
+
do_request
|
53
|
+
response.status.should eq(400)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when username and password are specified' do
|
58
|
+
app {
|
59
|
+
OAuth2::Rack::Authentication::ResourceOwner::RequestParams.new(chained_app) do |opts|
|
60
|
+
authenticator.call(opts)
|
61
|
+
end
|
62
|
+
}
|
63
|
+
let(:credentials) { Hash[:username => 'user_x', :password => 'secret'] }
|
64
|
+
before { params.merge! credentials }
|
65
|
+
|
66
|
+
context 'and credentials are valid' do
|
67
|
+
it 'sets oauth2.resource_owner in env' do
|
68
|
+
authenticator.should_receive(:call).with(credentials).and_return(resource_owner)
|
69
|
+
chained_app.should_receive(:call).with(hash_including('oauth2.resource_owner' => resource_owner)).and_return(chained_app_response)
|
70
|
+
|
71
|
+
do_request
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'but credentials are invalid' do
|
76
|
+
it 'responds with 401 unauthorized' do
|
77
|
+
authenticator.should_receive(:call).with(credentials).and_return(nil)
|
78
|
+
chained_app.should_not_receive(:call)
|
79
|
+
|
80
|
+
do_request
|
81
|
+
|
82
|
+
response.status.should eq(401)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::Rack::Authorization::ClientCredentials::AccessTokenIssuer do
|
4
|
+
let(:client) { double('client') }
|
5
|
+
let(:opts) { Hash.new }
|
6
|
+
|
7
|
+
def do_request
|
8
|
+
post '/', opts
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'when client is authenticated' do
|
12
|
+
before { opts['oauth2.client'] = client }
|
13
|
+
|
14
|
+
context 'and grant_type is invalid' do
|
15
|
+
let(:params) { Hash[:grant_type => 'xclient_credentials'] }
|
16
|
+
before { opts[:params] = params }
|
17
|
+
|
18
|
+
it 'responds with invalid_request' do
|
19
|
+
do_request
|
20
|
+
|
21
|
+
response.status.should eq(400)
|
22
|
+
response_object['error'].should eq('invalid_request')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'and grant_type is valid' do
|
27
|
+
let(:params) { Hash[:grant_type => 'client_credentials'] }
|
28
|
+
before { opts[:params] = params }
|
29
|
+
|
30
|
+
context 'and issuer is not specified' do
|
31
|
+
it 'responds with unauthorized_client' do
|
32
|
+
do_request
|
33
|
+
|
34
|
+
response.status.should eq(400)
|
35
|
+
response_object['error'].should eq('unauthorized_client')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'and issuer is specified' do
|
40
|
+
let(:issuer) { double('issuer') }
|
41
|
+
let(:expected_find_opts) {
|
42
|
+
Hash[:grant_type => 'client_credentials',
|
43
|
+
:client => client,
|
44
|
+
:scope => nil]
|
45
|
+
}
|
46
|
+
|
47
|
+
app { OAuth2::Rack::Authorization::ClientCredentials::AccessTokenIssuer.new(chained_app) { |opts| issuer.call(opts) } }
|
48
|
+
|
49
|
+
context 'but token is not found for the client' do
|
50
|
+
context 'and error is returned' do
|
51
|
+
before {
|
52
|
+
issuer.should_receive(:call).with(expected_find_opts).and_return({'error' => 'customized_error'})
|
53
|
+
}
|
54
|
+
it 'responds with the that error' do
|
55
|
+
do_request
|
56
|
+
|
57
|
+
response.status.should eq(400)
|
58
|
+
response_object['error'].should eq('customized_error')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
context 'and nothing is returned' do
|
62
|
+
before {
|
63
|
+
issuer.should_receive(:call).with(expected_find_opts).and_return(nil)
|
64
|
+
}
|
65
|
+
it 'responds with unauthorized_client' do
|
66
|
+
do_request
|
67
|
+
|
68
|
+
response.status.should eq(400)
|
69
|
+
response_object['error'].should eq('unauthorized_client')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
context 'and token is found for the client' do
|
75
|
+
before {
|
76
|
+
issuer.should_receive(:call).with(expected_find_opts).and_return({:access_token => 'X'})
|
77
|
+
}
|
78
|
+
|
79
|
+
it 'responds with the found token' do
|
80
|
+
do_request
|
81
|
+
|
82
|
+
response.status.should eq(200)
|
83
|
+
response_object['access_token'].should eq('X')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'when client not authenticated' do
|
92
|
+
before { post '/' }
|
93
|
+
|
94
|
+
it 'responds with invalid_client' do
|
95
|
+
response.status.should eq(400)
|
96
|
+
response_object['error'].should eq('invalid_client')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OAuth2::Rack::Authorization::Password::AccessTokenIssuer do
|
4
|
+
let(:resource_owner) { double('resource_owner') }
|
5
|
+
let(:client) { double('client') }
|
6
|
+
let(:opts) { Hash.new }
|
7
|
+
|
8
|
+
def do_request
|
9
|
+
post '/', opts
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when resource_owner is authenticated' do
|
13
|
+
before { opts['oauth2.resource_owner'] = resource_owner }
|
14
|
+
|
15
|
+
context 'and grant_type is invalid' do
|
16
|
+
let(:params) { Hash[:grant_type => 'xpassword'] }
|
17
|
+
before { opts[:params] = params }
|
18
|
+
|
19
|
+
it 'responds with invalid_request' do
|
20
|
+
do_request
|
21
|
+
|
22
|
+
response.status.should eq(400)
|
23
|
+
response_object['error'].should eq('invalid_request')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'and grant_type is valid' do
|
28
|
+
let(:params) { Hash[:grant_type => 'password'] }
|
29
|
+
before { opts[:params] = params }
|
30
|
+
|
31
|
+
context 'and issuer is not specified' do
|
32
|
+
it 'responds with unauthorized_client' do
|
33
|
+
do_request
|
34
|
+
|
35
|
+
response.status.should eq(400)
|
36
|
+
response_object['error'].should eq('unauthorized_client')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'and issuer is specified' do
|
41
|
+
let(:issuer) { double('issuer') }
|
42
|
+
let(:expected_find_opts) {
|
43
|
+
Hash[:grant_type => 'password',
|
44
|
+
:resource_owner => resource_owner,
|
45
|
+
:client => nil,
|
46
|
+
:scope => nil]
|
47
|
+
}
|
48
|
+
|
49
|
+
app { OAuth2::Rack::Authorization::Password::AccessTokenIssuer.new(chained_app) { |opts| issuer.call(opts) } }
|
50
|
+
|
51
|
+
context 'but token is not found for the resource owner' do
|
52
|
+
context 'and error is returned' do
|
53
|
+
before {
|
54
|
+
issuer.should_receive(:call).with(expected_find_opts).and_return({'error' => 'customized_error'})
|
55
|
+
}
|
56
|
+
it 'responds with the that error' do
|
57
|
+
do_request
|
58
|
+
|
59
|
+
response.status.should eq(400)
|
60
|
+
response_object['error'].should eq('customized_error')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
context 'and nothing is returned' do
|
64
|
+
before {
|
65
|
+
issuer.should_receive(:call).with(expected_find_opts).and_return(nil)
|
66
|
+
}
|
67
|
+
it 'responds with unauthorized_client' do
|
68
|
+
do_request
|
69
|
+
|
70
|
+
response.status.should eq(400)
|
71
|
+
response_object['error'].should eq('unauthorized_client')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
context 'and token is found for the client' do
|
77
|
+
before {
|
78
|
+
issuer.should_receive(:call).with(expected_find_opts).and_return({:access_token => 'X'})
|
79
|
+
}
|
80
|
+
|
81
|
+
it 'responds with the found token' do
|
82
|
+
do_request
|
83
|
+
|
84
|
+
response.status.should eq(200)
|
85
|
+
response_object['access_token'].should eq('X')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when resource owner not authenticated' do
|
94
|
+
before { post '/' }
|
95
|
+
|
96
|
+
it 'responds with invalid_grant' do
|
97
|
+
response.status.should eq(400)
|
98
|
+
response_object['error'].should eq('invalid_grant')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
$: << File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'oauth2/rack'
|
3
|
+
require 'multi_json'
|
4
|
+
|
5
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
6
|
+
# in spec/support/ and its subdirectories.
|
7
|
+
Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f}
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.mock_with :rspec
|
11
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'rack/mock'
|
2
|
+
require 'rack/utils'
|
3
|
+
|
4
|
+
module RackTestHelper
|
5
|
+
def self.included(klass)
|
6
|
+
klass.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :response
|
10
|
+
|
11
|
+
def chained_app
|
12
|
+
if defined?(@original_chained_app)
|
13
|
+
@original_chained_app
|
14
|
+
else
|
15
|
+
@original_chained_app = instance_eval(&self.class.chained_app)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def app
|
20
|
+
if defined?(@original_app)
|
21
|
+
@original_app
|
22
|
+
else
|
23
|
+
@original_app = instance_eval(&self.class.app)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def request
|
28
|
+
@request ||= Rack::MockRequest.new(app)
|
29
|
+
end
|
30
|
+
|
31
|
+
def response_object
|
32
|
+
return unless response
|
33
|
+
|
34
|
+
case response['Content-Type']
|
35
|
+
when %r{^application/json}
|
36
|
+
MultiJson::decode(response.body)
|
37
|
+
when %r{^application/x-www-form-urlencoded}
|
38
|
+
Rack::Utils.new.parse(response.body)
|
39
|
+
else
|
40
|
+
response.body
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def get(uri, opts = {})
|
45
|
+
self.response = request.get(uri, opts)
|
46
|
+
end
|
47
|
+
def post(uri, opts = {})
|
48
|
+
self.response = request.post(uri, opts)
|
49
|
+
end
|
50
|
+
def put(uri, opts = {})
|
51
|
+
self.response = request.put(uri, opts)
|
52
|
+
end
|
53
|
+
def delete(uri, opts = {})
|
54
|
+
self.response = request.delete(uri, opts)
|
55
|
+
end
|
56
|
+
|
57
|
+
module ClassMethods
|
58
|
+
attr_reader :explicit_chained_app_block
|
59
|
+
def chained_app(&block)
|
60
|
+
block ? @explicit_chained_app_block = block : explicit_chained_app || implicit_chained_app
|
61
|
+
end
|
62
|
+
|
63
|
+
attr_reader :explicit_app_block
|
64
|
+
def app(&block)
|
65
|
+
block ? @explicit_app_block = block : explicit_app || implicit_app
|
66
|
+
end
|
67
|
+
|
68
|
+
def explicit_app
|
69
|
+
group = self
|
70
|
+
while group.respond_to?(:explicit_app_block)
|
71
|
+
return group.explicit_app_block if group.explicit_app_block
|
72
|
+
group = group.superclass
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def implicit_app
|
77
|
+
described = describes || description
|
78
|
+
Class === described ? proc { described.new(chained_app) } : proc { described }
|
79
|
+
end
|
80
|
+
|
81
|
+
def explicit_chained_app
|
82
|
+
group = self
|
83
|
+
while group.respond_to?(:explicit_chained_app_block)
|
84
|
+
return group.explicit_chained_app_block if group.explicit_chained_app_block
|
85
|
+
group = group.superclass
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def implicit_chained_app
|
90
|
+
proc { |env| [200, {"Content-Type" => 'text/html'}, ["<p>OK</p>"]] }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
RSpec.configure do |config|
|
96
|
+
config.include RackTestHelper
|
97
|
+
end
|
metadata
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oauth2-rack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ian Yang
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-08-26 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: multi_json
|
16
|
+
requirement: &8610000 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *8610000
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rack
|
27
|
+
requirement: &8609580 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *8609580
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rake
|
38
|
+
requirement: &8609160 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *8609160
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: &8608740 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *8608740
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: yard
|
60
|
+
requirement: &8608320 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *8608320
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: shotgun
|
71
|
+
requirement: &8607900 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *8607900
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: guard-rspec
|
82
|
+
requirement: &8607480 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *8607480
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: oauth2
|
93
|
+
requirement: &8607060 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *8607060
|
102
|
+
description: Rack middlewares for OAuth2 authorization server and resource server
|
103
|
+
email:
|
104
|
+
- me@iany.me
|
105
|
+
executables: []
|
106
|
+
extensions: []
|
107
|
+
extra_rdoc_files: []
|
108
|
+
files:
|
109
|
+
- .gitignore
|
110
|
+
- .rspec
|
111
|
+
- Gemfile
|
112
|
+
- Guardfile
|
113
|
+
- Rakefile
|
114
|
+
- config.ru
|
115
|
+
- examples/ruby-client/password_authorization.rb
|
116
|
+
- lib/oauth2-rack.rb
|
117
|
+
- lib/oauth2/rack.rb
|
118
|
+
- lib/oauth2/rack/authentication.rb
|
119
|
+
- lib/oauth2/rack/authentication/client.rb
|
120
|
+
- lib/oauth2/rack/authentication/client/http_basic.rb
|
121
|
+
- lib/oauth2/rack/authentication/client/request_params.rb
|
122
|
+
- lib/oauth2/rack/authentication/resource_owner.rb
|
123
|
+
- lib/oauth2/rack/authentication/resource_owner/request_params.rb
|
124
|
+
- lib/oauth2/rack/authorization.rb
|
125
|
+
- lib/oauth2/rack/authorization/client_credentials.rb
|
126
|
+
- lib/oauth2/rack/authorization/client_credentials/access_token_issuer.rb
|
127
|
+
- lib/oauth2/rack/authorization/password.rb
|
128
|
+
- lib/oauth2/rack/authorization/password/access_token_issuer.rb
|
129
|
+
- spec/oauth2/rack/authentication/client/http_basic_spec.rb
|
130
|
+
- spec/oauth2/rack/authentication/client/request_params_spec.rb
|
131
|
+
- spec/oauth2/rack/authentication/resource_owner/request_params_spec.rb
|
132
|
+
- spec/oauth2/rack/authorization/client_credentials/access_token_issuer_spec.rb
|
133
|
+
- spec/oauth2/rack/authorization/password/access_token_issuer_spec.rb
|
134
|
+
- spec/spec_helper.rb
|
135
|
+
- spec/support/rake_test_helper.rb
|
136
|
+
homepage: https://github.com/doitian/oauth2-rack
|
137
|
+
licenses: []
|
138
|
+
post_install_message:
|
139
|
+
rdoc_options: []
|
140
|
+
require_paths:
|
141
|
+
- lib
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
143
|
+
none: false
|
144
|
+
requirements:
|
145
|
+
- - ! '>='
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
|
+
none: false
|
150
|
+
requirements:
|
151
|
+
- - ! '>='
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
requirements: []
|
155
|
+
rubyforge_project: oauth2-rack
|
156
|
+
rubygems_version: 1.8.6
|
157
|
+
signing_key:
|
158
|
+
specification_version: 3
|
159
|
+
summary: Rack middlewares for OAuth2 authorization server and resource server
|
160
|
+
test_files:
|
161
|
+
- spec/oauth2/rack/authentication/client/http_basic_spec.rb
|
162
|
+
- spec/oauth2/rack/authentication/client/request_params_spec.rb
|
163
|
+
- spec/oauth2/rack/authentication/resource_owner/request_params_spec.rb
|
164
|
+
- spec/oauth2/rack/authorization/client_credentials/access_token_issuer_spec.rb
|
165
|
+
- spec/oauth2/rack/authorization/password/access_token_issuer_spec.rb
|
166
|
+
- spec/spec_helper.rb
|
167
|
+
- spec/support/rake_test_helper.rb
|