rack-oauth2-revibe 1.0.7
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/.document +5 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +78 -0
- data/Rakefile +25 -0
- data/VERSION +1 -0
- data/lib/rack/oauth2.rb +67 -0
- data/lib/rack/oauth2/access_token.rb +36 -0
- data/lib/rack/oauth2/access_token/authenticator.rb +24 -0
- data/lib/rack/oauth2/access_token/bearer.rb +11 -0
- data/lib/rack/oauth2/access_token/legacy.rb +23 -0
- data/lib/rack/oauth2/access_token/mac.rb +103 -0
- data/lib/rack/oauth2/access_token/mac/sha256_hex_verifier.rb +17 -0
- data/lib/rack/oauth2/access_token/mac/signature.rb +34 -0
- data/lib/rack/oauth2/access_token/mac/verifier.rb +44 -0
- data/lib/rack/oauth2/client.rb +139 -0
- data/lib/rack/oauth2/client/error.rb +14 -0
- data/lib/rack/oauth2/client/grant.rb +30 -0
- data/lib/rack/oauth2/client/grant/authorization_code.rb +12 -0
- data/lib/rack/oauth2/client/grant/client_credentials.rb +10 -0
- data/lib/rack/oauth2/client/grant/facebook_token.rb +12 -0
- data/lib/rack/oauth2/client/grant/password.rb +11 -0
- data/lib/rack/oauth2/client/grant/refresh_token.rb +11 -0
- data/lib/rack/oauth2/debugger.rb +3 -0
- data/lib/rack/oauth2/debugger/request_filter.rb +30 -0
- data/lib/rack/oauth2/server.rb +4 -0
- data/lib/rack/oauth2/server/abstract.rb +4 -0
- data/lib/rack/oauth2/server/abstract/error.rb +69 -0
- data/lib/rack/oauth2/server/abstract/handler.rb +20 -0
- data/lib/rack/oauth2/server/abstract/request.rb +29 -0
- data/lib/rack/oauth2/server/abstract/response.rb +15 -0
- data/lib/rack/oauth2/server/authorize.rb +117 -0
- data/lib/rack/oauth2/server/authorize/code.rb +39 -0
- data/lib/rack/oauth2/server/authorize/error.rb +71 -0
- data/lib/rack/oauth2/server/authorize/extension.rb +12 -0
- data/lib/rack/oauth2/server/authorize/extension/code_and_token.rb +39 -0
- data/lib/rack/oauth2/server/authorize/token.rb +43 -0
- data/lib/rack/oauth2/server/resource.rb +55 -0
- data/lib/rack/oauth2/server/resource/bearer.rb +47 -0
- data/lib/rack/oauth2/server/resource/bearer/error.rb +24 -0
- data/lib/rack/oauth2/server/resource/error.rb +81 -0
- data/lib/rack/oauth2/server/resource/mac.rb +36 -0
- data/lib/rack/oauth2/server/resource/mac/error.rb +24 -0
- data/lib/rack/oauth2/server/token.rb +87 -0
- data/lib/rack/oauth2/server/token/authorization_code.rb +28 -0
- data/lib/rack/oauth2/server/token/client_credentials.rb +23 -0
- data/lib/rack/oauth2/server/token/error.rb +54 -0
- data/lib/rack/oauth2/server/token/extension.rb +12 -0
- data/lib/rack/oauth2/server/token/extension/jwt.rb +37 -0
- data/lib/rack/oauth2/server/token/facebook_token.rb +27 -0
- data/lib/rack/oauth2/server/token/password.rb +27 -0
- data/lib/rack/oauth2/server/token/refresh_token.rb +26 -0
- data/lib/rack/oauth2/util.rb +58 -0
- data/rack-oauth2.gemspec +30 -0
- data/spec/helpers/time.rb +19 -0
- data/spec/helpers/webmock_helper.rb +41 -0
- data/spec/mock_response/blank +0 -0
- data/spec/mock_response/errors/invalid_request.json +4 -0
- data/spec/mock_response/resources/fake.txt +1 -0
- data/spec/mock_response/tokens/_Bearer.json +6 -0
- data/spec/mock_response/tokens/bearer.json +6 -0
- data/spec/mock_response/tokens/legacy.json +5 -0
- data/spec/mock_response/tokens/legacy.txt +1 -0
- data/spec/mock_response/tokens/legacy_without_expires_in.txt +1 -0
- data/spec/mock_response/tokens/mac.json +8 -0
- data/spec/mock_response/tokens/unknown.json +6 -0
- data/spec/rack/oauth2/access_token/authenticator_spec.rb +43 -0
- data/spec/rack/oauth2/access_token/bearer_spec.rb +18 -0
- data/spec/rack/oauth2/access_token/legacy_spec.rb +23 -0
- data/spec/rack/oauth2/access_token/mac/sha256_hex_verifier_spec.rb +28 -0
- data/spec/rack/oauth2/access_token/mac/signature_spec.rb +59 -0
- data/spec/rack/oauth2/access_token/mac/verifier_spec.rb +25 -0
- data/spec/rack/oauth2/access_token/mac_spec.rb +141 -0
- data/spec/rack/oauth2/access_token_spec.rb +69 -0
- data/spec/rack/oauth2/client/error_spec.rb +18 -0
- data/spec/rack/oauth2/client/grant/authorization_code_spec.rb +37 -0
- data/spec/rack/oauth2/client/grant/client_credentials_spec.rb +7 -0
- data/spec/rack/oauth2/client/grant/password_spec.rb +33 -0
- data/spec/rack/oauth2/client/grant/refresh_token_spec.rb +21 -0
- data/spec/rack/oauth2/client_spec.rb +287 -0
- data/spec/rack/oauth2/debugger/request_filter_spec.rb +33 -0
- data/spec/rack/oauth2/oauth2_spec.rb +74 -0
- data/spec/rack/oauth2/server/abstract/error_spec.rb +59 -0
- data/spec/rack/oauth2/server/authorize/code_spec.rb +57 -0
- data/spec/rack/oauth2/server/authorize/error_spec.rb +103 -0
- data/spec/rack/oauth2/server/authorize/extensions/code_and_token_spec.rb +60 -0
- data/spec/rack/oauth2/server/authorize/token_spec.rb +73 -0
- data/spec/rack/oauth2/server/authorize_spec.rb +214 -0
- data/spec/rack/oauth2/server/resource/bearer/error_spec.rb +52 -0
- data/spec/rack/oauth2/server/resource/bearer_spec.rb +123 -0
- data/spec/rack/oauth2/server/resource/error_spec.rb +147 -0
- data/spec/rack/oauth2/server/resource/mac/error_spec.rb +52 -0
- data/spec/rack/oauth2/server/resource/mac_spec.rb +119 -0
- data/spec/rack/oauth2/server/resource_spec.rb +23 -0
- data/spec/rack/oauth2/server/token/authorization_code_spec.rb +43 -0
- data/spec/rack/oauth2/server/token/client_credentials_spec.rb +23 -0
- data/spec/rack/oauth2/server/token/error_spec.rb +77 -0
- data/spec/rack/oauth2/server/token/password_spec.rb +37 -0
- data/spec/rack/oauth2/server/token/refresh_token_spec.rb +34 -0
- data/spec/rack/oauth2/server/token_spec.rb +134 -0
- data/spec/rack/oauth2/util_spec.rb +97 -0
- data/spec/spec_helper.rb +14 -0
- metadata +326 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
class AccessToken
|
4
|
+
class MAC
|
5
|
+
class Sha256HexVerifier < Verifier
|
6
|
+
attr_optional :raw_body
|
7
|
+
|
8
|
+
def calculate
|
9
|
+
return nil unless raw_body.present?
|
10
|
+
|
11
|
+
OpenSSL::Digest::SHA256.new.digest(raw_body).unpack('H*').first
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
class AccessToken
|
4
|
+
class MAC
|
5
|
+
class Signature < Verifier
|
6
|
+
attr_required :secret, :ts, :nonce, :method, :request_uri, :host, :port
|
7
|
+
attr_optional :ext, :query
|
8
|
+
|
9
|
+
def calculate
|
10
|
+
Rack::OAuth2::Util.base64_encode OpenSSL::HMAC.digest(
|
11
|
+
hash_generator,
|
12
|
+
secret,
|
13
|
+
normalized_request_string
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def normalized_request_string
|
18
|
+
[
|
19
|
+
ts.to_i,
|
20
|
+
nonce,
|
21
|
+
method.to_s.upcase,
|
22
|
+
request_uri,
|
23
|
+
host,
|
24
|
+
port,
|
25
|
+
ext || '',
|
26
|
+
nil
|
27
|
+
].join("\n")
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
class AccessToken
|
4
|
+
class MAC
|
5
|
+
class Verifier
|
6
|
+
include AttrRequired, AttrOptional
|
7
|
+
attr_required :algorithm
|
8
|
+
|
9
|
+
class VerificationFailed < StandardError; end
|
10
|
+
|
11
|
+
def initialize(attributes = {})
|
12
|
+
(required_attributes + optional_attributes).each do |key|
|
13
|
+
self.send :"#{key}=", attributes[key]
|
14
|
+
end
|
15
|
+
attr_missing!
|
16
|
+
rescue AttrRequired::AttrMissing => e
|
17
|
+
raise VerificationFailed.new("#{self.class.name.demodulize} Invalid: #{e.message}")
|
18
|
+
end
|
19
|
+
|
20
|
+
def verify!(expected)
|
21
|
+
if expected == self.calculate
|
22
|
+
:verified
|
23
|
+
else
|
24
|
+
raise VerificationFailed.new("#{self.class.name.demodulize} Invalid")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def hash_generator
|
31
|
+
case algorithm.to_s
|
32
|
+
when 'hmac-sha-1'
|
33
|
+
OpenSSL::Digest::SHA1.new
|
34
|
+
when 'hmac-sha-256'
|
35
|
+
OpenSSL::Digest::SHA256.new
|
36
|
+
else
|
37
|
+
raise 'Unsupported Algorithm'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
class Client
|
4
|
+
include AttrRequired, AttrOptional
|
5
|
+
attr_required :identifier
|
6
|
+
attr_optional :secret, :redirect_uri, :scheme, :host, :port, :authorization_endpoint, :token_endpoint
|
7
|
+
|
8
|
+
def initialize(attributes = {})
|
9
|
+
(required_attributes + optional_attributes).each do |key|
|
10
|
+
self.send :"#{key}=", attributes[key]
|
11
|
+
end
|
12
|
+
@grant = Grant::ClientCredentials.new
|
13
|
+
@authorization_endpoint ||= '/oauth2/authorize'
|
14
|
+
@token_endpoint ||= '/oauth2/token'
|
15
|
+
attr_missing!
|
16
|
+
end
|
17
|
+
|
18
|
+
def authorization_uri(params = {})
|
19
|
+
params[:response_type] ||= :code
|
20
|
+
params[:response_type] = Array(params[:response_type]).join(' ')
|
21
|
+
params[:scope] = Array(params[:scope]).join(' ')
|
22
|
+
Util.redirect_uri absolute_uri_for(authorization_endpoint), :query, params.merge(
|
23
|
+
:client_id => self.identifier,
|
24
|
+
:redirect_uri => self.redirect_uri
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def authorization_code=(code)
|
29
|
+
@grant = Grant::AuthorizationCode.new(
|
30
|
+
:code => code,
|
31
|
+
:redirect_uri => self.redirect_uri
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def resource_owner_credentials=(credentials)
|
36
|
+
@grant = Grant::Password.new(
|
37
|
+
:username => credentials.first,
|
38
|
+
:password => credentials.last
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def facebook_token=(token)
|
43
|
+
@grant = Grant::FacebookToken.new(
|
44
|
+
:facebook_token => token
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def refresh_token=(token)
|
49
|
+
@grant = Grant::RefreshToken.new(
|
50
|
+
:refresh_token => token
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
def access_token!(*args)
|
55
|
+
headers, params = {}, @grant.as_json
|
56
|
+
|
57
|
+
# NOTE:
|
58
|
+
# Using Array#estract_options! for backward compatibility.
|
59
|
+
# Until v1.0.5, the first argument was 'client_auth_method' in scalar.
|
60
|
+
options = args.extract_options!
|
61
|
+
client_auth_method = args.first || options[:client_auth_method] || :basic
|
62
|
+
|
63
|
+
params[:scope] = Array(options[:scope]).join(' ') if options[:scope].present?
|
64
|
+
|
65
|
+
if secret && client_auth_method == :basic
|
66
|
+
cred = ["#{identifier}:#{secret}"].pack('m').tr("\n", '')
|
67
|
+
headers.merge!(
|
68
|
+
'Authorization' => "Basic #{cred}"
|
69
|
+
)
|
70
|
+
else
|
71
|
+
params.merge!(
|
72
|
+
:client_id => identifier,
|
73
|
+
:client_secret => secret
|
74
|
+
)
|
75
|
+
end
|
76
|
+
handle_response do
|
77
|
+
Rack::OAuth2.http_client.post(
|
78
|
+
absolute_uri_for(token_endpoint),
|
79
|
+
Util.compact_hash(params),
|
80
|
+
headers
|
81
|
+
)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def absolute_uri_for(endpoint)
|
88
|
+
_endpoint_ = Util.parse_uri endpoint
|
89
|
+
_endpoint_.scheme ||= self.scheme || 'https'
|
90
|
+
_endpoint_.host ||= self.host
|
91
|
+
_endpoint_.port ||= self.port
|
92
|
+
raise 'No Host Info' unless _endpoint_.host
|
93
|
+
_endpoint_.to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
def handle_response
|
97
|
+
response = yield
|
98
|
+
case response.status
|
99
|
+
when 200..201
|
100
|
+
handle_success_response response
|
101
|
+
else
|
102
|
+
handle_error_response response
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def handle_success_response(response)
|
107
|
+
token_hash = parse_json response.body
|
108
|
+
case token_hash[:token_type].try(:downcase)
|
109
|
+
when 'bearer'
|
110
|
+
AccessToken::Bearer.new(token_hash)
|
111
|
+
when 'mac'
|
112
|
+
AccessToken::MAC.new(token_hash)
|
113
|
+
when nil
|
114
|
+
AccessToken::Legacy.new(token_hash)
|
115
|
+
else
|
116
|
+
raise 'Unknown Token Type'
|
117
|
+
end
|
118
|
+
rescue MultiJson::DecodeError
|
119
|
+
# NOTE: Facebook support (They don't use JSON as token response)
|
120
|
+
AccessToken::Legacy.new Rack::Utils.parse_nested_query(response.body).with_indifferent_access
|
121
|
+
end
|
122
|
+
|
123
|
+
def handle_error_response(response)
|
124
|
+
error = parse_json response.body
|
125
|
+
raise Error.new(response.status, error)
|
126
|
+
rescue MultiJson::DecodeError
|
127
|
+
raise Error.new(response.status, :error => 'Unknown', :error_description => response.body)
|
128
|
+
end
|
129
|
+
|
130
|
+
def parse_json(raw_json)
|
131
|
+
# MultiJson.parse('') returns nil when using MultiJson::Adapters::JsonGem
|
132
|
+
MultiJson.load(raw_json).try(:with_indifferent_access) || {}
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
require 'rack/oauth2/client/error'
|
139
|
+
require 'rack/oauth2/client/grant'
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
class Client
|
4
|
+
class Error < StandardError
|
5
|
+
attr_accessor :status, :response
|
6
|
+
def initialize(status, response)
|
7
|
+
@status = status
|
8
|
+
@response = response
|
9
|
+
super response[:error_description]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
class Client
|
4
|
+
class Grant
|
5
|
+
include AttrRequired, AttrOptional
|
6
|
+
|
7
|
+
def initialize(attributes = {})
|
8
|
+
(required_attributes + optional_attributes).each do |key|
|
9
|
+
self.send "#{key}=", attributes[key]
|
10
|
+
end
|
11
|
+
attr_missing!
|
12
|
+
end
|
13
|
+
|
14
|
+
def as_json(options = {})
|
15
|
+
(required_attributes + optional_attributes).inject({
|
16
|
+
:grant_type => self.class.name.demodulize.underscore.to_sym
|
17
|
+
}) do |hash, key|
|
18
|
+
hash.merge! key => self.send(key)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'rack/oauth2/client/grant/authorization_code'
|
27
|
+
require 'rack/oauth2/client/grant/password'
|
28
|
+
require 'rack/oauth2/client/grant/facebook_token'
|
29
|
+
require 'rack/oauth2/client/grant/client_credentials'
|
30
|
+
require 'rack/oauth2/client/grant/refresh_token'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
module Debugger
|
4
|
+
class RequestFilter
|
5
|
+
# Callback called in HTTPClient (before sending a request)
|
6
|
+
# request:: HTTP::Message
|
7
|
+
def filter_request(request)
|
8
|
+
started = "======= [Rack::OAuth2] HTTP REQUEST STARTED ======="
|
9
|
+
log started, request.dump
|
10
|
+
end
|
11
|
+
|
12
|
+
# Callback called in HTTPClient (after received a response)
|
13
|
+
# request:: HTTP::Message
|
14
|
+
# response:: HTTP::Message
|
15
|
+
def filter_response(request, response)
|
16
|
+
finished = "======= [Rack::OAuth2] HTTP REQUEST FINISHED ======="
|
17
|
+
log '-' * 50, response.dump, finished
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def log(*outputs)
|
23
|
+
outputs.each do |output|
|
24
|
+
OAuth2.logger.info output
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
module Server
|
4
|
+
module Abstract
|
5
|
+
class Error < StandardError
|
6
|
+
attr_accessor :status, :error, :description, :uri, :realm
|
7
|
+
|
8
|
+
def initialize(status, error, description = nil, options = {})
|
9
|
+
@status = status
|
10
|
+
@error = error
|
11
|
+
@description = description
|
12
|
+
@uri = options[:uri]
|
13
|
+
@realm = options[:realm]
|
14
|
+
super [error, description].compact.join(' :: ')
|
15
|
+
end
|
16
|
+
|
17
|
+
def protocol_params
|
18
|
+
{
|
19
|
+
:error => error,
|
20
|
+
:error_description => description,
|
21
|
+
:error_uri => uri
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def finish
|
26
|
+
response = Rack::Response.new
|
27
|
+
response.status = status
|
28
|
+
yield response if block_given?
|
29
|
+
unless response.redirect?
|
30
|
+
response.header['Content-Type'] = 'application/json'
|
31
|
+
response.write MultiJson.dump(Util.compact_hash(protocol_params))
|
32
|
+
end
|
33
|
+
response.finish
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class BadRequest < Error
|
38
|
+
def initialize(error = :bad_request, description = nil, options = {})
|
39
|
+
super 400, error, description, options
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Unauthorized < Error
|
44
|
+
def initialize(error = :unauthorized, description = nil, options = {})
|
45
|
+
super 401, error, description, options
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Forbidden < Error
|
50
|
+
def initialize(error = :forbidden, description = nil, options = {})
|
51
|
+
super 403, error, description, options
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class ServerError < Error
|
56
|
+
def initialize(error = :forbidden, description = nil, options = {})
|
57
|
+
super 500, error, description, options
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class TemporarilyUnavailable < Error
|
62
|
+
def initialize(error = :forbidden, description = nil, options = {})
|
63
|
+
super 503, error, description, options
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|