oauth2-aptible 0.9.4.aptible
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/CONTRIBUTING.md +18 -0
- data/LICENSE.md +20 -0
- data/README.md +137 -0
- data/Rakefile +39 -0
- data/lib/oauth2.rb +10 -0
- data/lib/oauth2/access_token.rb +173 -0
- data/lib/oauth2/client.rb +173 -0
- data/lib/oauth2/error.rb +24 -0
- data/lib/oauth2/response.rb +90 -0
- data/lib/oauth2/strategy/assertion.rb +72 -0
- data/lib/oauth2/strategy/auth_code.rb +33 -0
- data/lib/oauth2/strategy/base.rb +16 -0
- data/lib/oauth2/strategy/client_credentials.rb +36 -0
- data/lib/oauth2/strategy/implicit.rb +29 -0
- data/lib/oauth2/strategy/password.rb +27 -0
- data/lib/oauth2/version.rb +15 -0
- data/oauth2.gemspec +27 -0
- data/spec/helper.rb +29 -0
- data/spec/oauth2/access_token_spec.rb +172 -0
- data/spec/oauth2/client_spec.rb +205 -0
- data/spec/oauth2/response_spec.rb +101 -0
- data/spec/oauth2/strategy/assertion_spec.rb +56 -0
- data/spec/oauth2/strategy/auth_code_spec.rb +88 -0
- data/spec/oauth2/strategy/base_spec.rb +7 -0
- data/spec/oauth2/strategy/client_credentials_spec.rb +81 -0
- data/spec/oauth2/strategy/implicit_spec.rb +28 -0
- data/spec/oauth2/strategy/password_spec.rb +57 -0
- metadata +174 -0
data/lib/oauth2/error.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module OAuth2
|
2
|
+
class Error < StandardError
|
3
|
+
attr_reader :response, :code, :description
|
4
|
+
|
5
|
+
# standard error values include:
|
6
|
+
# :invalid_request, :invalid_client, :invalid_token, :invalid_grant, :unsupported_grant_type, :invalid_scope
|
7
|
+
def initialize(response)
|
8
|
+
response.error = self
|
9
|
+
@response = response
|
10
|
+
|
11
|
+
message = []
|
12
|
+
|
13
|
+
if response.parsed.is_a?(Hash)
|
14
|
+
@code = response.parsed['error']
|
15
|
+
@description = response.parsed['error_description']
|
16
|
+
message << "#{@code}: #{@description}"
|
17
|
+
end
|
18
|
+
|
19
|
+
message << response.body
|
20
|
+
|
21
|
+
super(message.join("\n"))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'multi_xml'
|
3
|
+
require 'rack'
|
4
|
+
|
5
|
+
module OAuth2
|
6
|
+
# OAuth2::Response class
|
7
|
+
class Response
|
8
|
+
attr_reader :response
|
9
|
+
attr_accessor :error, :options
|
10
|
+
|
11
|
+
# Adds a new content type parser.
|
12
|
+
#
|
13
|
+
# @param [Symbol] key A descriptive symbol key such as :json or :query.
|
14
|
+
# @param [Array] One or more mime types to which this parser applies.
|
15
|
+
# @yield [String] A block returning parsed content.
|
16
|
+
def self.register_parser(key, mime_types, &block)
|
17
|
+
key = key.to_sym
|
18
|
+
PARSERS[key] = block
|
19
|
+
Array(mime_types).each do |mime_type|
|
20
|
+
CONTENT_TYPES[mime_type] = key
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Initializes a Response instance
|
25
|
+
#
|
26
|
+
# @param [Faraday::Response] response The Faraday response instance
|
27
|
+
# @param [Hash] opts options in which to initialize the instance
|
28
|
+
# @option opts [Symbol] :parse (:automatic) how to parse the response body. one of :query (for x-www-form-urlencoded),
|
29
|
+
# :json, or :automatic (determined by Content-Type response header)
|
30
|
+
def initialize(response, opts = {})
|
31
|
+
@response = response
|
32
|
+
@options = {:parse => :automatic}.merge(opts)
|
33
|
+
end
|
34
|
+
|
35
|
+
# The HTTP response headers
|
36
|
+
def headers
|
37
|
+
response.headers
|
38
|
+
end
|
39
|
+
|
40
|
+
# The HTTP response status code
|
41
|
+
def status
|
42
|
+
response.status
|
43
|
+
end
|
44
|
+
|
45
|
+
# The HTTP response body
|
46
|
+
def body
|
47
|
+
response.body || ''
|
48
|
+
end
|
49
|
+
|
50
|
+
# Procs that, when called, will parse a response body according
|
51
|
+
# to the specified format.
|
52
|
+
PARSERS = {
|
53
|
+
:query => lambda { |body| Rack::Utils.parse_query(body) },
|
54
|
+
:text => lambda { |body| body }
|
55
|
+
}
|
56
|
+
|
57
|
+
# Content type assignments for various potential HTTP content types.
|
58
|
+
CONTENT_TYPES = {
|
59
|
+
'application/x-www-form-urlencoded' => :query,
|
60
|
+
'text/plain' => :text
|
61
|
+
}
|
62
|
+
|
63
|
+
# The parsed response body.
|
64
|
+
# Will attempt to parse application/x-www-form-urlencoded and
|
65
|
+
# application/json Content-Type response bodies
|
66
|
+
def parsed
|
67
|
+
return nil unless PARSERS.key?(parser)
|
68
|
+
@parsed ||= PARSERS[parser].call(body)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Attempts to determine the content type of the response.
|
72
|
+
def content_type
|
73
|
+
((response.headers.values_at('content-type', 'Content-Type').compact.first || '').split(';').first || '').strip
|
74
|
+
end
|
75
|
+
|
76
|
+
# Determines the parser that will be used to supply the content of #parsed
|
77
|
+
def parser
|
78
|
+
return options[:parse].to_sym if PARSERS.key?(options[:parse])
|
79
|
+
CONTENT_TYPES[content_type]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
OAuth2::Response.register_parser(:xml, ['text/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml']) do |body|
|
85
|
+
MultiXml.parse(body) rescue body # rubocop:disable RescueModifier
|
86
|
+
end
|
87
|
+
|
88
|
+
OAuth2::Response.register_parser(:json, ['application/json', 'text/javascript', 'application/hal+json']) do |body|
|
89
|
+
MultiJson.load(body) rescue body # rubocop:disable RescueModifier
|
90
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'jwt'
|
2
|
+
|
3
|
+
module OAuth2
|
4
|
+
module Strategy
|
5
|
+
# The Client Assertion Strategy
|
6
|
+
#
|
7
|
+
# @see http://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-07#section-3.1
|
8
|
+
#
|
9
|
+
# Sample usage:
|
10
|
+
# client = OAuth2::Client.new(client_id, client_secret,
|
11
|
+
# :site => 'http://localhost:8080')
|
12
|
+
#
|
13
|
+
# params = {:hmac_secret => "some secret",
|
14
|
+
# # or :private_key => "private key string",
|
15
|
+
# :iss => "http://localhost:3001",
|
16
|
+
# :sub => "me@here.com",
|
17
|
+
# :exp => Time.now.utc.to_i + 3600}
|
18
|
+
#
|
19
|
+
# access = client.assertion.get_token(params)
|
20
|
+
# access.token # actual access_token string
|
21
|
+
# access.get("/api/stuff") # making api calls with access token in header
|
22
|
+
#
|
23
|
+
class Assertion < Base
|
24
|
+
# Not used for this strategy
|
25
|
+
#
|
26
|
+
# @raise [NotImplementedError]
|
27
|
+
def authorize_url
|
28
|
+
fail(NotImplementedError, 'The authorization endpoint is not used in this strategy')
|
29
|
+
end
|
30
|
+
|
31
|
+
# Retrieve an access token given the specified client.
|
32
|
+
#
|
33
|
+
# @param [Hash] params assertion params
|
34
|
+
# pass either :hmac_secret or :private_key, but not both.
|
35
|
+
#
|
36
|
+
# params :hmac_secret, secret string.
|
37
|
+
# params :private_key, private key string.
|
38
|
+
#
|
39
|
+
# params :iss, issuer
|
40
|
+
# params :aud, audience, optional
|
41
|
+
# params :sub, principal, current user
|
42
|
+
# params :exp, expired at, in seconds, like Time.now.utc.to_i + 3600
|
43
|
+
#
|
44
|
+
# @param [Hash] opts options
|
45
|
+
def get_token(params = {}, opts = {})
|
46
|
+
hash = build_request(params)
|
47
|
+
@client.get_token(hash, opts.merge('refresh_token' => nil))
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_request(params)
|
51
|
+
assertion = build_assertion(params)
|
52
|
+
{:grant_type => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
53
|
+
:assertion => assertion,
|
54
|
+
:scope => params[:scope]
|
55
|
+
}.merge(client_params)
|
56
|
+
end
|
57
|
+
|
58
|
+
def build_assertion(params)
|
59
|
+
claims = {:iss => params[:iss],
|
60
|
+
:aud => params[:aud],
|
61
|
+
:sub => params[:sub],
|
62
|
+
:exp => params[:exp]
|
63
|
+
}
|
64
|
+
if params[:hmac_secret]
|
65
|
+
JWT.encode(claims, params[:hmac_secret], params[:algorithm] || 'HS256')
|
66
|
+
elsif params[:private_key]
|
67
|
+
JWT.encode(claims, params[:private_key], params[:algorithm] || 'RS256')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module OAuth2
|
2
|
+
module Strategy
|
3
|
+
# The Authorization Code Strategy
|
4
|
+
#
|
5
|
+
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1
|
6
|
+
class AuthCode < Base
|
7
|
+
# The required query parameters for the authorize URL
|
8
|
+
#
|
9
|
+
# @param [Hash] params additional query parameters
|
10
|
+
def authorize_params(params = {})
|
11
|
+
params.merge('response_type' => 'code', 'client_id' => @client.id)
|
12
|
+
end
|
13
|
+
|
14
|
+
# The authorization URL endpoint of the provider
|
15
|
+
#
|
16
|
+
# @param [Hash] params additional query parameters for the URL
|
17
|
+
def authorize_url(params = {})
|
18
|
+
@client.authorize_url(authorize_params.merge(params))
|
19
|
+
end
|
20
|
+
|
21
|
+
# Retrieve an access token given the specified validation code.
|
22
|
+
#
|
23
|
+
# @param [String] code The Authorization Code value
|
24
|
+
# @param [Hash] params additional params
|
25
|
+
# @param [Hash] opts options
|
26
|
+
# @note that you must also provide a :redirect_uri with most OAuth 2.0 providers
|
27
|
+
def get_token(code, params = {}, opts = {})
|
28
|
+
params = {'grant_type' => 'authorization_code', 'code' => code}.merge(client_params).merge(params)
|
29
|
+
@client.get_token(params, opts)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module OAuth2
|
2
|
+
module Strategy
|
3
|
+
class Base
|
4
|
+
def initialize(client)
|
5
|
+
@client = client
|
6
|
+
end
|
7
|
+
|
8
|
+
# The OAuth client_id and client_secret
|
9
|
+
#
|
10
|
+
# @return [Hash]
|
11
|
+
def client_params
|
12
|
+
{'client_id' => @client.id, 'client_secret' => @client.secret}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module OAuth2
|
4
|
+
module Strategy
|
5
|
+
# The Client Credentials Strategy
|
6
|
+
#
|
7
|
+
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
|
8
|
+
class ClientCredentials < Base
|
9
|
+
# Not used for this strategy
|
10
|
+
#
|
11
|
+
# @raise [NotImplementedError]
|
12
|
+
def authorize_url
|
13
|
+
fail(NotImplementedError, 'The authorization endpoint is not used in this strategy')
|
14
|
+
end
|
15
|
+
|
16
|
+
# Retrieve an access token given the specified client.
|
17
|
+
#
|
18
|
+
# @param [Hash] params additional params
|
19
|
+
# @param [Hash] opts options
|
20
|
+
def get_token(params = {}, opts = {})
|
21
|
+
request_body = opts.delete('auth_scheme') == 'request_body'
|
22
|
+
params.merge!('grant_type' => 'client_credentials')
|
23
|
+
params.merge!(request_body ? client_params : {:headers => {'Authorization' => authorization(client_params['client_id'], client_params['client_secret'])}})
|
24
|
+
@client.get_token(params, opts.merge('refresh_token' => nil))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the Authorization header value for Basic Authentication
|
28
|
+
#
|
29
|
+
# @param [String] The client ID
|
30
|
+
# @param [String] the client secret
|
31
|
+
def authorization(client_id, client_secret)
|
32
|
+
'Basic ' + Base64.encode64(client_id + ':' + client_secret).gsub("\n", '')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module OAuth2
|
2
|
+
module Strategy
|
3
|
+
# The Implicit Strategy
|
4
|
+
#
|
5
|
+
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-26#section-4.2
|
6
|
+
class Implicit < Base
|
7
|
+
# The required query parameters for the authorize URL
|
8
|
+
#
|
9
|
+
# @param [Hash] params additional query parameters
|
10
|
+
def authorize_params(params = {})
|
11
|
+
params.merge('response_type' => 'token', 'client_id' => @client.id)
|
12
|
+
end
|
13
|
+
|
14
|
+
# The authorization URL endpoint of the provider
|
15
|
+
#
|
16
|
+
# @param [Hash] params additional query parameters for the URL
|
17
|
+
def authorize_url(params = {})
|
18
|
+
@client.authorize_url(authorize_params.merge(params))
|
19
|
+
end
|
20
|
+
|
21
|
+
# Not used for this strategy
|
22
|
+
#
|
23
|
+
# @raise [NotImplementedError]
|
24
|
+
def get_token(*)
|
25
|
+
fail(NotImplementedError, 'The token is accessed differently in this strategy')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module OAuth2
|
2
|
+
module Strategy
|
3
|
+
# The Resource Owner Password Credentials Authorization Strategy
|
4
|
+
#
|
5
|
+
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.3
|
6
|
+
class Password < Base
|
7
|
+
# Not used for this strategy
|
8
|
+
#
|
9
|
+
# @raise [NotImplementedError]
|
10
|
+
def authorize_url
|
11
|
+
fail(NotImplementedError, 'The authorization endpoint is not used in this strategy')
|
12
|
+
end
|
13
|
+
|
14
|
+
# Retrieve an access token given the specified End User username and password.
|
15
|
+
#
|
16
|
+
# @param [String] username the End User username
|
17
|
+
# @param [String] password the End User password
|
18
|
+
# @param [Hash] params additional params
|
19
|
+
def get_token(username, password, params = {}, opts = {})
|
20
|
+
params = {'grant_type' => 'password',
|
21
|
+
'username' => username,
|
22
|
+
'password' => password}.merge(client_params).merge(params)
|
23
|
+
@client.get_token(params, opts)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/oauth2.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'oauth2/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.add_development_dependency 'bundler', '~> 1.0'
|
8
|
+
spec.add_dependency 'faraday', ['>= 0.8', '< 0.10']
|
9
|
+
spec.add_dependency 'multi_json', '~> 1.3'
|
10
|
+
spec.add_dependency 'multi_xml', '~> 0.5'
|
11
|
+
spec.add_dependency 'rack', '~> 1.2'
|
12
|
+
spec.add_dependency 'jwt', '~> 0.1.8'
|
13
|
+
spec.authors = ['Frank Macreery']
|
14
|
+
spec.description = %q{A Ruby wrapper for the OAuth 2.0 protocol built with a similar style to the original OAuth spec.}
|
15
|
+
spec.email = ['frank@macreery.com']
|
16
|
+
spec.files = %w(.document CONTRIBUTING.md LICENSE.md README.md Rakefile oauth2.gemspec)
|
17
|
+
spec.files += Dir.glob('lib/**/*.rb')
|
18
|
+
spec.files += Dir.glob('spec/**/*')
|
19
|
+
spec.homepage = 'http://github.com/fancyremarker/oauth2-aptible'
|
20
|
+
spec.licenses = ['MIT']
|
21
|
+
spec.name = 'oauth2-aptible'
|
22
|
+
spec.require_paths = ['lib']
|
23
|
+
spec.required_rubygems_version = '>= 1.3.5'
|
24
|
+
spec.summary = %q{A Ruby wrapper for the OAuth 2.0 protocol.}
|
25
|
+
spec.test_files = Dir.glob('spec/**/*')
|
26
|
+
spec.version = OAuth2::Version
|
27
|
+
end
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'coveralls'
|
3
|
+
|
4
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
5
|
+
SimpleCov::Formatter::HTMLFormatter,
|
6
|
+
Coveralls::SimpleCov::Formatter
|
7
|
+
]
|
8
|
+
|
9
|
+
SimpleCov.start do
|
10
|
+
add_filter '/spec/'
|
11
|
+
minimum_coverage(95.29)
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'oauth2'
|
15
|
+
require 'addressable/uri'
|
16
|
+
require 'rspec'
|
17
|
+
require 'rspec/autorun'
|
18
|
+
|
19
|
+
RSpec.configure do |config|
|
20
|
+
config.expect_with :rspec do |c|
|
21
|
+
c.syntax = :expect
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Faraday.default_adapter = :test
|
26
|
+
|
27
|
+
RSpec.configure do |conf|
|
28
|
+
include OAuth2
|
29
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
VERBS = [:get, :post, :put, :delete]
|
4
|
+
|
5
|
+
describe AccessToken do
|
6
|
+
let(:token) { 'monkey' }
|
7
|
+
let(:token_body) { MultiJson.encode(:access_token => 'foo', :expires_in => 600, :refresh_token => 'bar') }
|
8
|
+
let(:refresh_body) { MultiJson.encode(:access_token => 'refreshed_foo', :expires_in => 600, :refresh_token => 'refresh_bar') }
|
9
|
+
let(:client) do
|
10
|
+
Client.new('abc', 'def', :site => 'https://api.example.com') do |builder|
|
11
|
+
builder.request :url_encoded
|
12
|
+
builder.adapter :test do |stub|
|
13
|
+
VERBS.each do |verb|
|
14
|
+
stub.send(verb, '/token/header') { |env| [200, {}, env[:request_headers]['Authorization']] }
|
15
|
+
stub.send(verb, "/token/query?access_token=#{token}") { |env| [200, {}, Addressable::URI.parse(env[:url]).query_values['access_token']] }
|
16
|
+
stub.send(verb, '/token/body') { |env| [200, {}, env[:body]] }
|
17
|
+
end
|
18
|
+
stub.post('/oauth/token') { |env| [200, {'Content-Type' => 'application/json'}, refresh_body] }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
subject { AccessToken.new(client, token) }
|
24
|
+
|
25
|
+
describe '#initialize' do
|
26
|
+
it 'assigns client and token' do
|
27
|
+
expect(subject.client).to eq(client)
|
28
|
+
expect(subject.token).to eq(token)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'assigns extra params' do
|
32
|
+
target = AccessToken.new(client, token, 'foo' => 'bar')
|
33
|
+
expect(target.params).to include('foo')
|
34
|
+
expect(target.params['foo']).to eq('bar')
|
35
|
+
end
|
36
|
+
|
37
|
+
def assert_initialized_token(target)
|
38
|
+
expect(target.token).to eq(token)
|
39
|
+
expect(target).to be_expires
|
40
|
+
expect(target.params.keys).to include('foo')
|
41
|
+
expect(target.params['foo']).to eq('bar')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'initializes with a Hash' do
|
45
|
+
hash = {:access_token => token, :expires_at => Time.now.to_i + 200, 'foo' => 'bar'}
|
46
|
+
target = AccessToken.from_hash(client, hash)
|
47
|
+
assert_initialized_token(target)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'initalizes with a form-urlencoded key/value string' do
|
51
|
+
kvform = "access_token=#{token}&expires_at=#{Time.now.to_i + 200}&foo=bar"
|
52
|
+
target = AccessToken.from_kvform(client, kvform)
|
53
|
+
assert_initialized_token(target)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'sets options' do
|
57
|
+
target = AccessToken.new(client, token, :param_name => 'foo', :header_format => 'Bearer %', :mode => :body)
|
58
|
+
expect(target.options[:param_name]).to eq('foo')
|
59
|
+
expect(target.options[:header_format]).to eq('Bearer %')
|
60
|
+
expect(target.options[:mode]).to eq(:body)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'initializes with a string expires_at' do
|
64
|
+
hash = {:access_token => token, :expires_at => '1361396829', 'foo' => 'bar'}
|
65
|
+
target = AccessToken.from_hash(client, hash)
|
66
|
+
assert_initialized_token(target)
|
67
|
+
expect(target.expires_at).to be_a(Integer)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#request' do
|
72
|
+
context ':mode => :header' do
|
73
|
+
before do
|
74
|
+
subject.options[:mode] = :header
|
75
|
+
end
|
76
|
+
|
77
|
+
VERBS.each do |verb|
|
78
|
+
it "sends the token in the Authorization header for a #{verb.to_s.upcase} request" do
|
79
|
+
expect(subject.post('/token/header').body).to include(token)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context ':mode => :query' do
|
85
|
+
before do
|
86
|
+
subject.options[:mode] = :query
|
87
|
+
end
|
88
|
+
|
89
|
+
VERBS.each do |verb|
|
90
|
+
it "sends the token in the Authorization header for a #{verb.to_s.upcase} request" do
|
91
|
+
expect(subject.post('/token/query').body).to eq(token)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context ':mode => :body' do
|
97
|
+
before do
|
98
|
+
subject.options[:mode] = :body
|
99
|
+
end
|
100
|
+
|
101
|
+
VERBS.each do |verb|
|
102
|
+
it "sends the token in the Authorization header for a #{verb.to_s.upcase} request" do
|
103
|
+
expect(subject.post('/token/body').body.split('=').last).to eq(token)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#expires?' do
|
110
|
+
it 'is false if there is no expires_at' do
|
111
|
+
expect(AccessToken.new(client, token)).not_to be_expires
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'is true if there is an expires_in' do
|
115
|
+
expect(AccessToken.new(client, token, :refresh_token => 'abaca', :expires_in => 600)).to be_expires
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'is true if there is an expires_at' do
|
119
|
+
expect(AccessToken.new(client, token, :refresh_token => 'abaca', :expires_in => Time.now.getutc.to_i + 600)).to be_expires
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '#expired?' do
|
124
|
+
it 'is false if there is no expires_in or expires_at' do
|
125
|
+
expect(AccessToken.new(client, token)).not_to be_expired
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'is false if expires_in is in the future' do
|
129
|
+
expect(AccessToken.new(client, token, :refresh_token => 'abaca', :expires_in => 10_800)).not_to be_expired
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'is true if expires_at is in the past' do
|
133
|
+
access = AccessToken.new(client, token, :refresh_token => 'abaca', :expires_in => 600)
|
134
|
+
@now = Time.now + 10_800
|
135
|
+
allow(Time).to receive(:now).and_return(@now)
|
136
|
+
expect(access).to be_expired
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#refresh!' do
|
142
|
+
let(:access) do
|
143
|
+
AccessToken.new(client, token, :refresh_token => 'abaca',
|
144
|
+
:expires_in => 600,
|
145
|
+
:param_name => 'o_param')
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'returns a refresh token with appropriate values carried over' do
|
149
|
+
refreshed = access.refresh!
|
150
|
+
expect(access.client).to eq(refreshed.client)
|
151
|
+
expect(access.options[:param_name]).to eq(refreshed.options[:param_name])
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'with a nil refresh_token in the response' do
|
155
|
+
let(:refresh_body) { MultiJson.encode(:access_token => 'refreshed_foo', :expires_in => 600, :refresh_token => nil) }
|
156
|
+
|
157
|
+
it 'copies the refresh_token from the original token' do
|
158
|
+
refreshed = access.refresh!
|
159
|
+
|
160
|
+
expect(refreshed.refresh_token).to eq(access.refresh_token)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '#to_hash' do
|
166
|
+
it 'return a hash equals to the hash used to initialize access token' do
|
167
|
+
hash = {:access_token => token, :refresh_token => 'foobar', :expires_at => Time.now.to_i + 200, 'foo' => 'bar'}
|
168
|
+
access_token = AccessToken.from_hash(client, hash.clone)
|
169
|
+
expect(access_token.to_hash).to eq(hash)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|