Imported Upstream version 0.9.3
Cédric Boutillier
10 years ago
3 | 3 | 3. Add specs for your unimplemented feature or bug fix. |
4 | 4 | 4. Run `bundle exec rake spec`. If your specs pass, return to step 3. |
5 | 5 | 5. Implement your feature or bug fix. |
6 | 6. Run `bundle exec rake spec`. If your specs fail, return to step 5. | |
6 | 6. Run `bundle exec rake`. If your specs fail, return to step 5. | |
7 | 7 | 7. Run `open coverage/index.html`. If your changes are not completely covered |
8 | 8 | by your tests, return to step 3. |
9 | 8. Add, commit, and push your changes. | |
10 | 9. [Submit a pull request.][pr] | |
9 | 8. Add documentation for your feature or bug fix. | |
10 | 9. Run `bundle exec rake verify_measurements`. If your changes are not 100% | |
11 | documented, go back to step 8. | |
12 | 10. Commit and push your changes. | |
13 | 11. [Submit a pull request.][pr] | |
11 | 14 | |
12 | 15 | [fork]: http://help.github.com/fork-a-repo/ |
13 | 16 | [branch]: http://learn.github.com/p/branching.html |
0 | 0 | # OAuth2 |
1 | ||
1 | 2 | [![Gem Version](https://badge.fury.io/rb/oauth2.png)][gem] |
2 | 3 | [![Build Status](https://secure.travis-ci.org/intridea/oauth2.png?branch=master)][travis] |
3 | 4 | [![Dependency Status](https://gemnasium.com/intridea/oauth2.png?travis)][gemnasium] |
4 | 5 | [![Code Climate](https://codeclimate.com/github/intridea/oauth2.png)][codeclimate] |
6 | [![Coverage Status](https://coveralls.io/repos/intridea/oauth2/badge.png?branch=master)][coveralls] | |
5 | 7 | |
6 | 8 | [gem]: https://rubygems.org/gems/oauth2 |
7 | 9 | [travis]: http://travis-ci.org/intridea/oauth2 |
8 | 10 | [gemnasium]: https://gemnasium.com/intridea/oauth2 |
9 | 11 | [codeclimate]: https://codeclimate.com/github/intridea/oauth2 |
12 | [coveralls]: https://coveralls.io/r/intridea/oauth2 | |
10 | 13 | |
11 | 14 | A Ruby wrapper for the OAuth 2.0 specification. This is a work in progress, |
12 | 15 | being built first to solve the pragmatic process of connecting to existing |
13 | OAuth 2.0 endpoints (a.k.a. Facebook) with the goal of building it up to meet | |
16 | OAuth 2.0 endpoints (e.g. Facebook) with the goal of building it up to meet | |
14 | 17 | the entire specification over time. |
15 | 18 | |
16 | 19 | ## Installation |
17 | 20 | gem install oauth2 |
18 | ||
19 | To ensure the code you're installing hasn't been tampered with, it's | |
20 | recommended that you verify the signature. To do this, you need to add my | |
21 | public key as a trusted certificate (you only need to do this once): | |
22 | ||
23 | gem cert --add <(curl -Ls https://gist.github.com/sferik/4701180/raw/public_cert.pem) | |
24 | ||
25 | Then, install the gem with the high security trust policy: | |
26 | ||
27 | gem install oauth2 -P HighSecurity | |
28 | 21 | |
29 | 22 | ## Resources |
30 | 23 | * [View Source on GitHub][code] |
36 | 29 | [wiki]: https://wiki.github.com/intridea/oauth2 |
37 | 30 | |
38 | 31 | ## Usage Examples |
39 | require 'oauth2' | |
40 | client = OAuth2::Client.new('client_id', 'client_secret', :site => 'https://example.org') | |
41 | 32 | |
42 | client.auth_code.authorize_url(:redirect_uri => 'http://localhost:8080/oauth2/callback') | |
43 | # => "https://example.org/oauth/authorization?response_type=code&client_id=client_id&redirect_uri=http://localhost:8080/oauth2/callback" | |
33 | ```ruby | |
34 | require 'oauth2' | |
35 | client = OAuth2::Client.new('client_id', 'client_secret', :site => 'https://example.org') | |
44 | 36 | |
45 | token = client.auth_code.get_token('authorization_code_value', :redirect_uri => 'http://localhost:8080/oauth2/callback', :headers => {'Authorization' => 'Basic some_password'}) | |
46 | response = token.get('/api/resource', :params => { 'query_foo' => 'bar' }) | |
47 | response.class.name | |
48 | # => OAuth2::Response | |
37 | client.auth_code.authorize_url(:redirect_uri => 'http://localhost:8080/oauth2/callback') | |
38 | # => "https://example.org/oauth/authorization?response_type=code&client_id=client_id&redirect_uri=http://localhost:8080/oauth2/callback" | |
49 | 39 | |
40 | token = client.auth_code.get_token('authorization_code_value', :redirect_uri => 'http://localhost:8080/oauth2/callback', :headers => {'Authorization' => 'Basic some_password'}) | |
41 | response = token.get('/api/resource', :params => { 'query_foo' => 'bar' }) | |
42 | response.class.name | |
43 | # => OAuth2::Response | |
44 | ``` | |
50 | 45 | ## OAuth2::Response |
51 | 46 | The AccessToken methods #get, #post, #put and #delete and the generic #request |
52 | 47 | will return an instance of the #OAuth2::Response class. |
81 | 76 | authentication grant types have helper strategy classes that simplify client |
82 | 77 | use. They are available via the #auth_code, #implicit, #password, #client_credentials, and #assertion methods respectively. |
83 | 78 | |
84 | auth_url = client.auth_code.authorize_url(:redirect_uri => 'http://localhost:8080/oauth/callback') | |
85 | token = client.auth_code.get_token('code_value', :redirect_uri => 'http://localhost:8080/oauth/callback') | |
79 | ```ruby | |
80 | auth_url = client.auth_code.authorize_url(:redirect_uri => 'http://localhost:8080/oauth/callback') | |
81 | token = client.auth_code.get_token('code_value', :redirect_uri => 'http://localhost:8080/oauth/callback') | |
86 | 82 | |
87 | auth_url = client.implicit.authorize_url(:redirect_uri => 'http://localhost:8080/oauth/callback') | |
88 | # get the token params in the callback and | |
89 | token = OAuth2::AccessToken.from_kvform(client, query_string) | |
83 | auth_url = client.implicit.authorize_url(:redirect_uri => 'http://localhost:8080/oauth/callback') | |
84 | # get the token params in the callback and | |
85 | token = OAuth2::AccessToken.from_kvform(client, query_string) | |
90 | 86 | |
91 | token = client.password.get_token('username', 'password') | |
87 | token = client.password.get_token('username', 'password') | |
92 | 88 | |
93 | token = client.client_credentials.get_token | |
89 | token = client.client_credentials.get_token | |
94 | 90 | |
95 | token = client.assertion.get_token(assertion_params) | |
91 | token = client.assertion.get_token(assertion_params) | |
92 | ``` | |
96 | 93 | |
97 | 94 | If you want to specify additional headers to be sent out with the |
98 | 95 | request, add a 'headers' hash under 'params': |
99 | 96 | |
100 | token = client.auth_code.get_token('code_value', :redirect_uri => 'http://localhost:8080/oauth/callback', :headers => {'Some' => 'Header'}) | |
97 | ```ruby | |
98 | token = client.auth_code.get_token('code_value', :redirect_uri => 'http://localhost:8080/oauth/callback', :headers => {'Some' => 'Header'}) | |
99 | ``` | |
101 | 100 | |
102 | 101 | You can always use the #request method on the OAuth2::Client instance to make |
103 | 102 | requests for tokens for any Authentication grant type. |
110 | 109 | * Ruby 1.9.2 |
111 | 110 | * Ruby 1.9.3 |
112 | 111 | * Ruby 2.0.0 |
112 | * Ruby 2.1.0 | |
113 | 113 | * [JRuby][] |
114 | 114 | * [Rubinius][] |
115 | 115 |
3 | 3 | require 'rspec/core/rake_task' |
4 | 4 | RSpec::Core::RakeTask.new(:spec) |
5 | 5 | |
6 | task :default => :spec | |
7 | 6 | task :test => :spec |
8 | 7 | |
9 | 8 | namespace :doc do |
16 | 15 | rdoc.rdoc_files.include('README.md', 'LICENSE.md', 'lib/**/*.rb') |
17 | 16 | end |
18 | 17 | end |
18 | ||
19 | begin | |
20 | require 'rubocop/rake_task' | |
21 | Rubocop::RakeTask.new | |
22 | rescue LoadError | |
23 | task :rubocop do | |
24 | $stderr.puts 'Rubocop is disabled' | |
25 | end | |
26 | end | |
27 | ||
28 | require 'yardstick/rake/measurement' | |
29 | Yardstick::Rake::Measurement.new do |measurement| | |
30 | measurement.output = 'measurement/report.txt' | |
31 | end | |
32 | ||
33 | require 'yardstick/rake/verify' | |
34 | Yardstick::Rake::Verify.new do |verify| | |
35 | verify.threshold = 58.9 | |
36 | end | |
37 | ||
38 | task :default => [:spec, :rubocop, :verify_measurements] |
Binary diff not shown
Binary diff not shown
Binary diff not shown
9 | 9 | # @param [Hash] a hash of AccessToken property values |
10 | 10 | # @return [AccessToken] the initalized AccessToken |
11 | 11 | def from_hash(client, hash) |
12 | self.new(client, hash.delete('access_token') || hash.delete(:access_token), hash) | |
12 | new(client, hash.delete('access_token') || hash.delete(:access_token), hash) | |
13 | 13 | end |
14 | 14 | |
15 | 15 | # Initializes an AccessToken from a key/value application/x-www-form-urlencoded string |
35 | 35 | # @option opts [String] :header_format ('Bearer %s') the string format to use for the Authorization header |
36 | 36 | # @option opts [String] :param_name ('access_token') the parameter name to use for transmission of the |
37 | 37 | # Access Token value in :body or :query transmission mode |
38 | def initialize(client, token, opts={}) | |
38 | def initialize(client, token, opts = {}) | |
39 | 39 | @client = client |
40 | 40 | @token = token.to_s |
41 | 41 | [:refresh_token, :expires_in, :expires_at].each do |arg| |
76 | 76 | # |
77 | 77 | # @return [AccessToken] a new AccessToken |
78 | 78 | # @note options should be carried over to the new AccessToken |
79 | def refresh!(params={}) | |
80 | raise "A refresh_token is not available" unless refresh_token | |
79 | def refresh!(params = {}) | |
80 | fail('A refresh_token is not available') unless refresh_token | |
81 | 81 | params.merge!(:client_id => @client.id, |
82 | 82 | :client_secret => @client.secret, |
83 | 83 | :grant_type => 'refresh_token', |
88 | 88 | new_token |
89 | 89 | end |
90 | 90 | |
91 | # Convert AccessToken to a hash which can be used to rebuild itself with AccessToken.from_hash | |
92 | # | |
93 | # @return [Hash] a hash of AccessToken property values | |
94 | def to_hash | |
95 | params.merge(:access_token => token, :refresh_token => refresh_token, :expires_at => expires_at) | |
96 | end | |
97 | ||
91 | 98 | # Make a request with the Access Token |
92 | 99 | # |
93 | 100 | # @param [Symbol] verb the HTTP request method |
94 | 101 | # @param [String] path the HTTP URL path of the request |
95 | 102 | # @param [Hash] opts the options to make the request with |
96 | 103 | # @see Client#request |
97 | def request(verb, path, opts={}, &block) | |
98 | set_token(opts) | |
104 | def request(verb, path, opts = {}, &block) | |
105 | self.token = opts | |
99 | 106 | @client.request(verb, path, opts, &block) |
100 | 107 | end |
101 | 108 | |
102 | 109 | # Make a GET request with the Access Token |
103 | 110 | # |
104 | 111 | # @see AccessToken#request |
105 | def get(path, opts={}, &block) | |
112 | def get(path, opts = {}, &block) | |
106 | 113 | request(:get, path, opts, &block) |
107 | 114 | end |
108 | 115 | |
109 | 116 | # Make a POST request with the Access Token |
110 | 117 | # |
111 | 118 | # @see AccessToken#request |
112 | def post(path, opts={}, &block) | |
119 | def post(path, opts = {}, &block) | |
113 | 120 | request(:post, path, opts, &block) |
114 | 121 | end |
115 | 122 | |
116 | 123 | # Make a PUT request with the Access Token |
117 | 124 | # |
118 | 125 | # @see AccessToken#request |
119 | def put(path, opts={}, &block) | |
126 | def put(path, opts = {}, &block) | |
120 | 127 | request(:put, path, opts, &block) |
121 | 128 | end |
122 | 129 | |
123 | 130 | # Make a PATCH request with the Access Token |
124 | 131 | # |
125 | 132 | # @see AccessToken#request |
126 | def patch(path, opts={}, &block) | |
133 | def patch(path, opts = {}, &block) | |
127 | 134 | request(:patch, path, opts, &block) |
128 | 135 | end |
129 | 136 | |
130 | 137 | # Make a DELETE request with the Access Token |
131 | 138 | # |
132 | 139 | # @see AccessToken#request |
133 | def delete(path, opts={}, &block) | |
140 | def delete(path, opts = {}, &block) | |
134 | 141 | request(:delete, path, opts, &block) |
135 | 142 | end |
136 | 143 | |
137 | 144 | # Get the headers hash (includes Authorization token) |
138 | 145 | def headers |
139 | { 'Authorization' => options[:header_format] % token } | |
146 | {'Authorization' => options[:header_format] % token} | |
140 | 147 | end |
141 | 148 | |
142 | 149 | private |
143 | def set_token(opts) | |
150 | ||
151 | def token=(opts) # rubocop:disable MethodLength | |
144 | 152 | case options[:mode] |
145 | 153 | when :header |
146 | 154 | opts[:headers] ||= {} |
157 | 165 | end |
158 | 166 | # @todo support for multi-part (file uploads) |
159 | 167 | else |
160 | raise "invalid :mode option of #{options[:mode]}" | |
168 | fail("invalid :mode option of #{options[:mode]}") | |
161 | 169 | end |
162 | 170 | end |
163 | 171 | end |
22 | 22 | # @option opts [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error |
23 | 23 | # on responses with 400+ status codes |
24 | 24 | # @yield [builder] The Faraday connection builder |
25 | def initialize(client_id, client_secret, opts={}, &block) | |
25 | def initialize(client_id, client_secret, opts = {}, &block) | |
26 | _opts = opts.dup | |
26 | 27 | @id = client_id |
27 | 28 | @secret = client_secret |
28 | @site = opts.delete(:site) | |
29 | ssl = opts.delete(:ssl) | |
29 | @site = _opts.delete(:site) | |
30 | ssl = _opts.delete(:ssl) | |
30 | 31 | @options = {:authorize_url => '/oauth/authorize', |
31 | 32 | :token_url => '/oauth/token', |
32 | 33 | :token_method => :post, |
33 | 34 | :connection_opts => {}, |
34 | 35 | :connection_build => block, |
35 | 36 | :max_redirects => 5, |
36 | :raise_errors => true}.merge(opts) | |
37 | :raise_errors => true}.merge(_opts) | |
37 | 38 | @options[:connection_opts][:ssl] = ssl if ssl |
38 | 39 | end |
39 | 40 | |
59 | 60 | # The authorize endpoint URL of the OAuth2 provider |
60 | 61 | # |
61 | 62 | # @param [Hash] params additional query parameters |
62 | def authorize_url(params=nil) | |
63 | def authorize_url(params = nil) | |
63 | 64 | connection.build_url(options[:authorize_url], params).to_s |
64 | 65 | end |
65 | 66 | |
66 | 67 | # The token endpoint URL of the OAuth2 provider |
67 | 68 | # |
68 | 69 | # @param [Hash] params additional query parameters |
69 | def token_url(params=nil) | |
70 | def token_url(params = nil) | |
70 | 71 | connection.build_url(options[:token_url], params).to_s |
71 | 72 | end |
72 | 73 | |
82 | 83 | # code response for this request. Will default to client option |
83 | 84 | # @option opts [Symbol] :parse @see Response::initialize |
84 | 85 | # @yield [req] The Faraday request |
85 | def request(verb, url, opts={}) | |
86 | url = self.connection.build_url(url, opts[:params]).to_s | |
86 | def request(verb, url, opts = {}) # rubocop:disable CyclomaticComplexity, MethodLength | |
87 | url = connection.build_url(url, opts[:params]).to_s | |
87 | 88 | |
88 | 89 | response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req| |
89 | 90 | yield(req) if block_given? |
104 | 105 | # on non-redirecting 3xx statuses, just return the response |
105 | 106 | response |
106 | 107 | when 400..599 |
107 | e = Error.new(response) | |
108 | raise e if opts[:raise_errors] || options[:raise_errors] | |
109 | response.error = e | |
108 | error = Error.new(response) | |
109 | fail(error) if opts.fetch(:raise_errors, options[:raise_errors]) | |
110 | response.error = error | |
110 | 111 | response |
111 | 112 | else |
112 | raise Error.new(response), "Unhandled status code value of #{response.status}" | |
113 | error = Error.new(response) | |
114 | fail(error, "Unhandled status code value of #{response.status}") | |
113 | 115 | end |
114 | 116 | end |
115 | 117 | |
117 | 119 | # |
118 | 120 | # @param [Hash] params a Hash of params for the token endpoint |
119 | 121 | # @param [Hash] access token options, to pass to the AccessToken object |
122 | # @param [Class] class of access token for easier subclassing OAuth2::AccessToken | |
120 | 123 | # @return [AccessToken] the initalized AccessToken |
121 | def get_token(params, access_token_opts={}) | |
124 | def get_token(params, access_token_opts = {}, access_token_class = AccessToken) | |
122 | 125 | opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)} |
123 | 126 | if options[:token_method] == :post |
124 | 127 | headers = params.delete(:headers) |
129 | 132 | opts[:params] = params |
130 | 133 | end |
131 | 134 | response = request(options[:token_method], token_url, opts) |
132 | raise Error.new(response) if options[:raise_errors] && !(response.parsed.is_a?(Hash) && response.parsed['access_token']) | |
133 | AccessToken.from_hash(self, response.parsed.merge(access_token_opts)) | |
135 | error = Error.new(response) | |
136 | fail(error) if options[:raise_errors] && !(response.parsed.is_a?(Hash) && response.parsed['access_token']) | |
137 | access_token_class.from_hash(self, response.parsed.merge(access_token_opts)) | |
134 | 138 | end |
135 | 139 | |
136 | 140 | # The Authorization Code strategy |
0 | 0 | require 'multi_json' |
1 | require 'multi_xml' | |
1 | 2 | require 'rack' |
2 | 3 | |
3 | 4 | module OAuth2 |
25 | 26 | # @param [Hash] opts options in which to initialize the instance |
26 | 27 | # @option opts [Symbol] :parse (:automatic) how to parse the response body. one of :query (for x-www-form-urlencoded), |
27 | 28 | # :json, or :automatic (determined by Content-Type response header) |
28 | def initialize(response, opts={}) | |
29 | def initialize(response, opts = {}) | |
29 | 30 | @response = response |
30 | 31 | @options = {:parse => :automatic}.merge(opts) |
31 | 32 | end |
48 | 49 | # Procs that, when called, will parse a response body according |
49 | 50 | # to the specified format. |
50 | 51 | PARSERS = { |
51 | # Can't reliably detect whether MultiJson responds to load, since it's | |
52 | # a reserved word. Use adapter as a proxy for new features. | |
53 | :json => lambda{ |body| MultiJson.respond_to?(:adapter) ? MultiJson.load(body) : MultiJson.decode(body) rescue body }, | |
54 | :query => lambda{ |body| Rack::Utils.parse_query(body) }, | |
55 | :text => lambda{ |body| body } | |
52 | :json => lambda { |body| MultiJson.load(body) rescue body }, # rubocop:disable RescueModifier | |
53 | :query => lambda { |body| Rack::Utils.parse_query(body) }, | |
54 | :text => lambda { |body| body } | |
56 | 55 | } |
57 | 56 | |
58 | 57 | # Content type assignments for various potential HTTP content types. |
84 | 83 | end |
85 | 84 | end |
86 | 85 | |
87 | begin | |
88 | require 'multi_xml' | |
89 | OAuth2::Response.register_parser(:xml, ['text/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml']) do |body| | |
90 | MultiXml.parse(body) rescue body | |
91 | end | |
92 | rescue LoadError; end | |
86 | OAuth2::Response.register_parser(:xml, ['text/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml']) do |body| | |
87 | MultiXml.parse(body) rescue body # rubocop:disable RescueModifier | |
88 | end |
0 | require 'httpauth' | |
1 | 0 | require 'jwt' |
2 | 1 | |
3 | 2 | module OAuth2 |
25 | 24 | # |
26 | 25 | # @raise [NotImplementedError] |
27 | 26 | def authorize_url |
28 | raise NotImplementedError, "The authorization endpoint is not used in this strategy" | |
27 | fail(NotImplementedError, 'The authorization endpoint is not used in this strategy') | |
29 | 28 | end |
30 | 29 | |
31 | 30 | # Retrieve an access token given the specified client. |
42 | 41 | # params :exp, expired at, in seconds, like Time.now.utc.to_i + 3600 |
43 | 42 | # |
44 | 43 | # @param [Hash] opts options |
45 | def get_token(params={}, opts={}) | |
44 | def get_token(params = {}, opts = {}) | |
46 | 45 | hash = build_request(params) |
47 | 46 | @client.get_token(hash, opts.merge('refresh_token' => nil)) |
48 | 47 | end |
49 | 48 | |
50 | 49 | def build_request(params) |
51 | 50 | assertion = build_assertion(params) |
52 | {:grant_type => "assertion", | |
53 | :assertion_type => "urn:ietf:params:oauth:grant-type:jwt-bearer", | |
51 | {:grant_type => 'assertion', | |
52 | :assertion_type => 'urn:ietf:params:oauth:grant-type:jwt-bearer', | |
54 | 53 | :assertion => assertion, |
55 | 54 | :scope => params[:scope] |
56 | 55 | }.merge(client_params) |
63 | 62 | :exp => params[:exp] |
64 | 63 | } |
65 | 64 | if params[:hmac_secret] |
66 | jwt_assertion = JWT.encode(claims, params[:hmac_secret], "HS256") | |
65 | JWT.encode(claims, params[:hmac_secret], 'HS256') | |
67 | 66 | elsif params[:private_key] |
68 | jwt_assertion = JWT.encode(claims, params[:private_key], "RS256") | |
67 | JWT.encode(claims, params[:private_key], 'RS256') | |
69 | 68 | end |
70 | 69 | end |
71 | 70 | end |
72 | 71 | end |
73 | 72 | end |
74 |
6 | 6 | # The required query parameters for the authorize URL |
7 | 7 | # |
8 | 8 | # @param [Hash] params additional query parameters |
9 | def authorize_params(params={}) | |
9 | def authorize_params(params = {}) | |
10 | 10 | params.merge('response_type' => 'code', 'client_id' => @client.id) |
11 | 11 | end |
12 | 12 | |
13 | 13 | # The authorization URL endpoint of the provider |
14 | 14 | # |
15 | 15 | # @param [Hash] params additional query parameters for the URL |
16 | def authorize_url(params={}) | |
16 | def authorize_url(params = {}) | |
17 | 17 | @client.authorize_url(authorize_params.merge(params)) |
18 | 18 | end |
19 | 19 | |
23 | 23 | # @param [Hash] params additional params |
24 | 24 | # @param [Hash] opts options |
25 | 25 | # @note that you must also provide a :redirect_uri with most OAuth 2.0 providers |
26 | def get_token(code, params={}, opts={}) | |
26 | def get_token(code, params = {}, opts = {}) | |
27 | 27 | params = {'grant_type' => 'authorization_code', 'code' => code}.merge(client_params).merge(params) |
28 | 28 | @client.get_token(params, opts) |
29 | 29 | end |
0 | require 'httpauth' | |
0 | require 'base64' | |
1 | 1 | |
2 | 2 | module OAuth2 |
3 | 3 | module Strategy |
9 | 9 | # |
10 | 10 | # @raise [NotImplementedError] |
11 | 11 | def authorize_url |
12 | raise NotImplementedError, "The authorization endpoint is not used in this strategy" | |
12 | fail(NotImplementedError, 'The authorization endpoint is not used in this strategy') | |
13 | 13 | end |
14 | 14 | |
15 | 15 | # Retrieve an access token given the specified client. |
16 | 16 | # |
17 | 17 | # @param [Hash] params additional params |
18 | 18 | # @param [Hash] opts options |
19 | def get_token(params={}, opts={}) | |
19 | def get_token(params = {}, opts = {}) | |
20 | 20 | request_body = opts.delete('auth_scheme') == 'request_body' |
21 | 21 | params.merge!('grant_type' => 'client_credentials') |
22 | params.merge!(request_body ? client_params : {:headers => {'Authorization' => HTTPAuth::Basic.pack_authorization(client_params['client_id'], client_params['client_secret'])}}) | |
22 | params.merge!(request_body ? client_params : {:headers => {'Authorization' => authorization(client_params['client_id'], client_params['client_secret'])}}) | |
23 | 23 | @client.get_token(params, opts.merge('refresh_token' => nil)) |
24 | end | |
25 | ||
26 | # Returns the Authorization header value for Basic Authentication | |
27 | # | |
28 | # @param [String] The client ID | |
29 | # @param [String] the client secret | |
30 | def authorization(client_id, client_secret) | |
31 | 'Basic ' + Base64.encode64(client_id + ':' + client_secret).gsub("\n", '') | |
24 | 32 | end |
25 | 33 | end |
26 | 34 | end |
6 | 6 | # The required query parameters for the authorize URL |
7 | 7 | # |
8 | 8 | # @param [Hash] params additional query parameters |
9 | def authorize_params(params={}) | |
9 | def authorize_params(params = {}) | |
10 | 10 | params.merge('response_type' => 'token', 'client_id' => @client.id) |
11 | 11 | end |
12 | 12 | |
13 | 13 | # The authorization URL endpoint of the provider |
14 | 14 | # |
15 | 15 | # @param [Hash] params additional query parameters for the URL |
16 | def authorize_url(params={}) | |
16 | def authorize_url(params = {}) | |
17 | 17 | @client.authorize_url(authorize_params.merge(params)) |
18 | 18 | end |
19 | 19 | |
21 | 21 | # |
22 | 22 | # @raise [NotImplementedError] |
23 | 23 | def get_token(*) |
24 | raise NotImplementedError, "The token is accessed differently in this strategy" | |
24 | fail(NotImplementedError, 'The token is accessed differently in this strategy') | |
25 | 25 | end |
26 | 26 | end |
27 | 27 | end |
7 | 7 | # |
8 | 8 | # @raise [NotImplementedError] |
9 | 9 | def authorize_url |
10 | raise NotImplementedError, "The authorization endpoint is not used in this strategy" | |
10 | fail(NotImplementedError, 'The authorization endpoint is not used in this strategy') | |
11 | 11 | end |
12 | 12 | |
13 | 13 | # Retrieve an access token given the specified End User username and password. |
15 | 15 | # @param [String] username the End User username |
16 | 16 | # @param [String] password the End User password |
17 | 17 | # @param [Hash] params additional params |
18 | def get_token(username, password, params={}, opts={}) | |
18 | def get_token(username, password, params = {}, opts = {}) | |
19 | 19 | params = {'grant_type' => 'password', |
20 | 20 | 'username' => username, |
21 | 21 | 'password' => password}.merge(client_params).merge(params) |
0 | 0 | module OAuth2 |
1 | 1 | class Version |
2 | MAJOR = 0 unless defined? MAJOR | |
3 | MINOR = 9 unless defined? MINOR | |
4 | PATCH = 1 unless defined? PATCH | |
5 | PRE = nil unless defined? PRE | |
2 | MAJOR = 0 | |
3 | MINOR = 9 | |
4 | PATCH = 3 | |
5 | PRE = nil | |
6 | 6 | |
7 | 7 | class << self |
8 | ||
9 | 8 | # @return [String] |
10 | 9 | def to_s |
11 | 10 | [MAJOR, MINOR, PATCH, PRE].compact.join('.') |
12 | 11 | end |
13 | ||
14 | 12 | end |
15 | ||
16 | 13 | end |
17 | 14 | end |
0 | &3¤ß0wQ0{Ce1>ªð¤ÆA#Ò÷Iøõ²Î9uþ ü_ÔåØÜÔc>í5n+ÊïÅÝCVò®æ~þë&$#2wab£¡Oõ¹KiUy£zy#};½*¶5¥w*QÓ=±Û´ZG cÆZl¸<!kÀ¹Eݨê6¾(Ø[ª:F_iMÑïúÍ | |
1 | ]ÉSËI¼¬dìÀQжÜ1ð0S9W§|ò1H`^ÀÀ('¹ÆT/uÁ#1Q52Å;àêsâʦaLúì2@¤¶jfgòÁ·ëÿN»Û¯ ⏎ | |
2 | sเว⏎ | |
0 | uHFï3,ìÕ"ß vÏ2{>têAéjäOâB'úÙ=G)º/I5/!¼vuÖ$Úþå2Rà¥Ð[B<z¼ ~è®ïNüCj¤måjehÍÇèÈvKÈ0hlÆ#Ä:DH-¢#LmÍzZX!ERÅ 1¡IøÇb8iWàË m´QaúÈ | |
1 | 0ºy3CTMþMP0}KØûy¯»µÔ¬ÌÊ ¹þ®ïT7³"ç§åWLI%¿/M | |
2 | ┐щa_ўнс?Ф┐░ЮhIэF¤ИsС╦┐Y=іlЪ#kAД.8╣▀њMB░E┴~┴S⏎ |
0 | 0 | --- !ruby/object:Gem::Specification |
1 | 1 | name: oauth2 |
2 | 2 | version: !ruby/object:Gem::Version |
3 | version: 0.9.1 | |
4 | prerelease: | |
3 | version: 0.9.3 | |
5 | 4 | platform: ruby |
6 | 5 | authors: |
7 | 6 | - Michael Bleigh |
9 | 8 | autorequire: |
10 | 9 | bindir: bin |
11 | 10 | cert_chain: |
12 | - !binary |- | |
13 | LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhhZ0F3SUJB | |
14 | Z0lCQURBTkJna3Foa2lHOXcwQkFRVUZBREE5TVE4d0RRWURWUVFEREFaelpt | |
15 | VnkKYVdzeEZUQVRCZ29Ka2lhSmsvSXNaQUVaRmdWbmJXRnBiREVUTUJFR0Nn | |
16 | bVNKb21UOGl4a0FSa1dBMk52YlRBZQpGdzB4TXpBeU1ETXhNREF5TWpkYUZ3 | |
17 | MHhOREF5TURNeE1EQXlNamRhTUQweER6QU5CZ05WQkFNTUJuTm1aWEpwCmF6 | |
18 | RVZNQk1HQ2dtU0pvbVQ4aXhrQVJrV0JXZHRZV2xzTVJNd0VRWUtDWkltaVpQ | |
19 | eUxHUUJHUllEWTI5dE1JSUIKSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4 | |
20 | QU1JSUJDZ0tDQVFFQWwweDVkeDh1S3hpN1Rrckl1eUJVVEpWQgp2MW85M25V | |
21 | QjlqL3k0TTk2Z1Yycll3QWNpMUpQQnNlTmQ2RnliempvM1lHdUhsN0VRSHVT | |
22 | SE5hZjFwMmx4ZXcvCnk2MEpYSUpCQmdQY0RLL0tDUDROVUhvZm0wamZvWUQr | |
23 | SDV1TkpmSENOcTcvWnNUeE90RTNSYTkyczBCQ01UcG0Kd0JNTWxXUjVNdGRF | |
24 | aElZdUJPNFhobmVqWWdIMEwvN0JMMmx5bW50Vm5zci9hZ2RRb29qUUNOMUlR | |
25 | bXNSSnZyUgpkdVpSTzN0WnZvSW8xcEJjNEpFZWhEdXFDZXlCZ1BMT3FNb0t0 | |
26 | UWxvbGQxVFFzMWtXVUJLN0tXTUZFaEtDL0tnCnp5ektSSFFvOXlEWXdPdllu | |
27 | Z29CTFkrVC9sd0NUNGR5c3NkaHpSYmZueEFoYUt1NFNBc3NJd2FDMDF5Vm93 | |
28 | SUQKQVFBQm96a3dOekFKQmdOVkhSTUVBakFBTUIwR0ExVWREZ1FXQkJTMHJ1 | |
29 | RGZSYWs1Y2kxT3BETlgvWmRERWtJcwppVEFMQmdOVkhROEVCQU1DQkxBd0RR | |
30 | WUpLb1pJaHZjTkFRRUZCUUFEZ2dFQkFISFNNcy9NUDBzT2FMa0V2NEpvCnp2 | |
31 | a20zcW41QTZ0MHZhSHg3NzRjbWVqeU1VKzV3eVN4UmV6c3BMN1VMaDlOZXVL | |
32 | Mk9oVStPZTNUcHFyQWc1VEsKUjhHUUlMblZ1MkZlbUdBNnNBa1BEbGNQdGdB | |
33 | NmllSTE5UFpPRjZIVkxtYy9JRC9kUC9OZ1pXV3pFZXFRS21jSwoyK0hNK1NF | |
34 | RURoWmtTY1lla3c0Wk9lMTY0WnRaRzgxNm9BdjV4MHBHaXRTSWt1bVVwN1Y4 | |
35 | aUVaLzZlaHI3WTllClhPZzRlZXVuNUwvSmptakFSb1cya05kdmtSRDNjMkVl | |
36 | U0xxV3ZRUnNCbHlwSGZoczZKSnVMbHlaUEdoVTNSL3YKU2YzbFZLcEJDV2dS | |
37 | cEdUdnk0NVhWcEIrNTl5MzNQSm1FdVExUFRFT1l2UXlhbzlVS01BQWFBTi83 | |
38 | cVdRdGpsMApobHc9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K | |
39 | date: 2013-02-20 00:00:00.000000000 Z | |
11 | - | | |
12 | -----BEGIN CERTIFICATE----- | |
13 | MIIDLjCCAhagAwIBAgIBADANBgkqhkiG9w0BAQUFADA9MQ8wDQYDVQQDDAZzZmVy | |
14 | aWsxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2NvbTAe | |
15 | Fw0xMzAyMDMxMDAyMjdaFw0xNDAyMDMxMDAyMjdaMD0xDzANBgNVBAMMBnNmZXJp | |
16 | azEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29tMIIB | |
17 | IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0x5dx8uKxi7TkrIuyBUTJVB | |
18 | v1o93nUB9j/y4M96gV2rYwAci1JPBseNd6Fybzjo3YGuHl7EQHuSHNaf1p2lxew/ | |
19 | y60JXIJBBgPcDK/KCP4NUHofm0jfoYD+H5uNJfHCNq7/ZsTxOtE3Ra92s0BCMTpm | |
20 | wBMMlWR5MtdEhIYuBO4XhnejYgH0L/7BL2lymntVnsr/agdQoojQCN1IQmsRJvrR | |
21 | duZRO3tZvoIo1pBc4JEehDuqCeyBgPLOqMoKtQlold1TQs1kWUBK7KWMFEhKC/Kg | |
22 | zyzKRHQo9yDYwOvYngoBLY+T/lwCT4dyssdhzRbfnxAhaKu4SAssIwaC01yVowID | |
23 | AQABozkwNzAJBgNVHRMEAjAAMB0GA1UdDgQWBBS0ruDfRak5ci1OpDNX/ZdDEkIs | |
24 | iTALBgNVHQ8EBAMCBLAwDQYJKoZIhvcNAQEFBQADggEBAHHSMs/MP0sOaLkEv4Jo | |
25 | zvkm3qn5A6t0vaHx774cmejyMU+5wySxRezspL7ULh9NeuK2OhU+Oe3TpqrAg5TK | |
26 | R8GQILnVu2FemGA6sAkPDlcPtgA6ieI19PZOF6HVLmc/ID/dP/NgZWWzEeqQKmcK | |
27 | 2+HM+SEEDhZkScYekw4ZOe164ZtZG816oAv5x0pGitSIkumUp7V8iEZ/6ehr7Y9e | |
28 | XOg4eeun5L/JjmjARoW2kNdvkRD3c2EeSLqWvQRsBlypHfhs6JJuLlyZPGhU3R/v | |
29 | Sf3lVKpBCWgRpGTvy45XVpB+59y33PJmEuQ1PTEOYvQyao9UKMAAaAN/7qWQtjl0 | |
30 | hlw= | |
31 | -----END CERTIFICATE----- | |
32 | date: 2014-01-16 00:00:00.000000000 Z | |
40 | 33 | dependencies: |
41 | 34 | - !ruby/object:Gem::Dependency |
42 | 35 | name: bundler |
43 | 36 | requirement: !ruby/object:Gem::Requirement |
44 | none: false | |
45 | 37 | requirements: |
46 | - - ~> | |
38 | - - "~>" | |
47 | 39 | - !ruby/object:Gem::Version |
48 | 40 | version: '1.0' |
49 | 41 | type: :development |
50 | 42 | prerelease: false |
51 | 43 | version_requirements: !ruby/object:Gem::Requirement |
52 | none: false | |
53 | 44 | requirements: |
54 | - - ~> | |
45 | - - "~>" | |
55 | 46 | - !ruby/object:Gem::Version |
56 | 47 | version: '1.0' |
57 | 48 | - !ruby/object:Gem::Dependency |
58 | 49 | name: faraday |
59 | 50 | requirement: !ruby/object:Gem::Requirement |
60 | none: false | |
61 | 51 | requirements: |
62 | - - ~> | |
52 | - - ">=" | |
63 | 53 | - !ruby/object:Gem::Version |
64 | 54 | version: '0.8' |
55 | - - "<" | |
56 | - !ruby/object:Gem::Version | |
57 | version: '0.10' | |
65 | 58 | type: :runtime |
66 | 59 | prerelease: false |
67 | 60 | version_requirements: !ruby/object:Gem::Requirement |
68 | none: false | |
69 | 61 | requirements: |
70 | - - ~> | |
62 | - - ">=" | |
71 | 63 | - !ruby/object:Gem::Version |
72 | 64 | version: '0.8' |
65 | - - "<" | |
66 | - !ruby/object:Gem::Version | |
67 | version: '0.10' | |
73 | 68 | - !ruby/object:Gem::Dependency |
74 | name: httpauth | |
69 | name: multi_json | |
75 | 70 | requirement: !ruby/object:Gem::Requirement |
76 | none: false | |
77 | 71 | requirements: |
78 | - - ~> | |
72 | - - "~>" | |
79 | 73 | - !ruby/object:Gem::Version |
80 | version: '0.1' | |
74 | version: '1.3' | |
81 | 75 | type: :runtime |
82 | 76 | prerelease: false |
83 | 77 | version_requirements: !ruby/object:Gem::Requirement |
84 | none: false | |
85 | 78 | requirements: |
86 | - - ~> | |
79 | - - "~>" | |
87 | 80 | - !ruby/object:Gem::Version |
88 | version: '0.1' | |
89 | - !ruby/object:Gem::Dependency | |
90 | name: multi_json | |
91 | requirement: !ruby/object:Gem::Requirement | |
92 | none: false | |
93 | requirements: | |
94 | - - ~> | |
95 | - !ruby/object:Gem::Version | |
96 | version: '1.0' | |
97 | type: :runtime | |
98 | prerelease: false | |
99 | version_requirements: !ruby/object:Gem::Requirement | |
100 | none: false | |
101 | requirements: | |
102 | - - ~> | |
103 | - !ruby/object:Gem::Version | |
104 | version: '1.0' | |
81 | version: '1.3' | |
105 | 82 | - !ruby/object:Gem::Dependency |
106 | 83 | name: multi_xml |
107 | 84 | requirement: !ruby/object:Gem::Requirement |
108 | none: false | |
109 | 85 | requirements: |
110 | - - ~> | |
86 | - - "~>" | |
111 | 87 | - !ruby/object:Gem::Version |
112 | 88 | version: '0.5' |
113 | 89 | type: :runtime |
114 | 90 | prerelease: false |
115 | 91 | version_requirements: !ruby/object:Gem::Requirement |
116 | none: false | |
117 | 92 | requirements: |
118 | - - ~> | |
93 | - - "~>" | |
119 | 94 | - !ruby/object:Gem::Version |
120 | 95 | version: '0.5' |
121 | 96 | - !ruby/object:Gem::Dependency |
122 | 97 | name: rack |
123 | 98 | requirement: !ruby/object:Gem::Requirement |
124 | none: false | |
125 | 99 | requirements: |
126 | - - ~> | |
100 | - - "~>" | |
127 | 101 | - !ruby/object:Gem::Version |
128 | 102 | version: '1.2' |
129 | 103 | type: :runtime |
130 | 104 | prerelease: false |
131 | 105 | version_requirements: !ruby/object:Gem::Requirement |
132 | none: false | |
133 | 106 | requirements: |
134 | - - ~> | |
107 | - - "~>" | |
135 | 108 | - !ruby/object:Gem::Version |
136 | 109 | version: '1.2' |
137 | 110 | - !ruby/object:Gem::Dependency |
138 | 111 | name: jwt |
139 | 112 | requirement: !ruby/object:Gem::Requirement |
140 | none: false | |
141 | 113 | requirements: |
142 | - - ~> | |
114 | - - "~>" | |
143 | 115 | - !ruby/object:Gem::Version |
144 | version: 0.1.4 | |
116 | version: 0.1.8 | |
145 | 117 | type: :runtime |
146 | 118 | prerelease: false |
147 | 119 | version_requirements: !ruby/object:Gem::Requirement |
148 | none: false | |
149 | 120 | requirements: |
150 | - - ~> | |
121 | - - "~>" | |
151 | 122 | - !ruby/object:Gem::Version |
152 | version: 0.1.4 | |
123 | version: 0.1.8 | |
153 | 124 | description: A Ruby wrapper for the OAuth 2.0 protocol built with a similar style |
154 | 125 | to the original OAuth spec. |
155 | 126 | email: |
159 | 130 | extensions: [] |
160 | 131 | extra_rdoc_files: [] |
161 | 132 | files: |
162 | - .document | |
133 | - ".document" | |
163 | 134 | - CONTRIBUTING.md |
164 | 135 | - LICENSE.md |
165 | 136 | - README.md |
166 | 137 | - Rakefile |
167 | - oauth2.gemspec | |
138 | - lib/oauth2.rb | |
168 | 139 | - lib/oauth2/access_token.rb |
169 | 140 | - lib/oauth2/client.rb |
170 | 141 | - lib/oauth2/error.rb |
176 | 147 | - lib/oauth2/strategy/implicit.rb |
177 | 148 | - lib/oauth2/strategy/password.rb |
178 | 149 | - lib/oauth2/version.rb |
179 | - lib/oauth2.rb | |
150 | - oauth2.gemspec | |
180 | 151 | - spec/helper.rb |
181 | 152 | - spec/oauth2/access_token_spec.rb |
182 | 153 | - spec/oauth2/client_spec.rb |
190 | 161 | homepage: http://github.com/intridea/oauth2 |
191 | 162 | licenses: |
192 | 163 | - MIT |
164 | metadata: {} | |
193 | 165 | post_install_message: |
194 | 166 | rdoc_options: [] |
195 | 167 | require_paths: |
196 | 168 | - lib |
197 | 169 | required_ruby_version: !ruby/object:Gem::Requirement |
198 | none: false | |
199 | 170 | requirements: |
200 | - - ! '>=' | |
171 | - - ">=" | |
201 | 172 | - !ruby/object:Gem::Version |
202 | 173 | version: '0' |
203 | 174 | required_rubygems_version: !ruby/object:Gem::Requirement |
204 | none: false | |
205 | 175 | requirements: |
206 | - - ! '>=' | |
176 | - - ">=" | |
207 | 177 | - !ruby/object:Gem::Version |
208 | version: 1.3.6 | |
178 | version: 1.3.5 | |
209 | 179 | requirements: [] |
210 | 180 | rubyforge_project: |
211 | rubygems_version: 1.8.23 | |
181 | rubygems_version: 2.2.0 | |
212 | 182 | signing_key: |
213 | specification_version: 3 | |
183 | specification_version: 4 | |
214 | 184 | summary: A Ruby wrapper for the OAuth 2.0 protocol. |
215 | 185 | test_files: |
216 | 186 | - spec/helper.rb |
4 | 4 | |
5 | 5 | Gem::Specification.new do |spec| |
6 | 6 | spec.add_development_dependency 'bundler', '~> 1.0' |
7 | spec.add_dependency 'faraday', '~> 0.8' | |
8 | spec.add_dependency 'httpauth', '~> 0.1' | |
9 | spec.add_dependency 'multi_json', '~> 1.0' | |
7 | spec.add_dependency 'faraday', ['>= 0.8', '< 0.10'] | |
8 | spec.add_dependency 'multi_json', '~> 1.3' | |
10 | 9 | spec.add_dependency 'multi_xml', '~> 0.5' |
11 | 10 | spec.add_dependency 'rack', '~> 1.2' |
12 | spec.add_dependency 'jwt', '~> 0.1.4' | |
13 | spec.authors = ["Michael Bleigh", "Erik Michaels-Ober"] | |
11 | spec.add_dependency 'jwt', '~> 0.1.8' | |
12 | spec.authors = ['Michael Bleigh', 'Erik Michaels-Ober'] | |
14 | 13 | spec.cert_chain = %w(certs/sferik.pem) |
15 | 14 | spec.description = %q{A Ruby wrapper for the OAuth 2.0 protocol built with a similar style to the original OAuth spec.} |
16 | 15 | spec.email = ['michael@intridea.com', 'sferik@gmail.com'] |
17 | 16 | spec.files = %w(.document CONTRIBUTING.md LICENSE.md README.md Rakefile oauth2.gemspec) |
18 | spec.files += Dir.glob("lib/**/*.rb") | |
19 | spec.files += Dir.glob("spec/**/*") | |
17 | spec.files += Dir.glob('lib/**/*.rb') | |
18 | spec.files += Dir.glob('spec/**/*') | |
20 | 19 | spec.homepage = 'http://github.com/intridea/oauth2' |
21 | 20 | spec.licenses = ['MIT'] |
22 | 21 | spec.name = 'oauth2' |
23 | 22 | spec.require_paths = ['lib'] |
24 | spec.required_rubygems_version = '>= 1.3.6' | |
25 | spec.signing_key = File.expand_path("~/.gem/private_key.pem") if $0 =~ /gem\z/ | |
23 | spec.required_rubygems_version = '>= 1.3.5' | |
24 | spec.signing_key = File.expand_path('~/.gem/private_key.pem') if $PROGRAM_NAME =~ /gem\z/ | |
26 | 25 | spec.summary = %q{A Ruby wrapper for the OAuth 2.0 protocol.} |
27 | spec.test_files = Dir.glob("spec/**/*") | |
26 | spec.test_files = Dir.glob('spec/**/*') | |
28 | 27 | spec.version = OAuth2::Version |
29 | 28 | end |
0 | unless ENV['CI'] | |
1 | require 'simplecov' | |
2 | SimpleCov.start do | |
3 | add_filter 'spec' | |
4 | end | |
0 | require 'simplecov' | |
1 | require 'coveralls' | |
2 | ||
3 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ | |
4 | SimpleCov::Formatter::HTMLFormatter, | |
5 | Coveralls::SimpleCov::Formatter | |
6 | ] | |
7 | ||
8 | SimpleCov.start do | |
9 | add_filter '/spec/' | |
10 | minimum_coverage(95.29) | |
5 | 11 | end |
6 | 12 | |
7 | 13 | require 'oauth2' |
2 | 2 | VERBS = [:get, :post, :put, :delete] |
3 | 3 | |
4 | 4 | describe AccessToken do |
5 | let(:token) {'monkey'} | |
6 | let(:token_body) {MultiJson.encode(:access_token => 'foo', :expires_in => 600, :refresh_token => 'bar')} | |
7 | let(:refresh_body) {MultiJson.encode(:access_token => 'refreshed_foo', :expires_in => 600, :refresh_token => 'refresh_bar')} | |
5 | let(:token) { 'monkey' } | |
6 | let(:token_body) { MultiJson.encode(:access_token => 'foo', :expires_in => 600, :refresh_token => 'bar') } | |
7 | let(:refresh_body) { MultiJson.encode(:access_token => 'refreshed_foo', :expires_in => 600, :refresh_token => 'refresh_bar') } | |
8 | 8 | let(:client) do |
9 | 9 | Client.new('abc', 'def', :site => 'https://api.example.com') do |builder| |
10 | 10 | builder.request :url_encoded |
11 | 11 | builder.adapter :test do |stub| |
12 | 12 | VERBS.each do |verb| |
13 | stub.send(verb, '/token/header') {|env| [200, {}, env[:request_headers]['Authorization']]} | |
14 | stub.send(verb, "/token/query?access_token=#{token}") {|env| [200, {}, Addressable::URI.parse(env[:url]).query_values['access_token']]} | |
15 | stub.send(verb, '/token/body') {|env| [200, {}, env[:body]]} | |
13 | stub.send(verb, '/token/header') { |env| [200, {}, env[:request_headers]['Authorization']] } | |
14 | stub.send(verb, "/token/query?access_token=#{token}") { |env| [200, {}, Addressable::URI.parse(env[:url]).query_values['access_token']] } | |
15 | stub.send(verb, '/token/body') { |env| [200, {}, env[:body]] } | |
16 | 16 | end |
17 | stub.post('/oauth/token') {|env| [200, {'Content-Type' => 'application/json'}, refresh_body]} | |
17 | stub.post('/oauth/token') { |env| [200, {'Content-Type' => 'application/json'}, refresh_body] } | |
18 | 18 | end |
19 | 19 | end |
20 | 20 | end |
21 | 21 | |
22 | subject {AccessToken.new(client, token)} | |
22 | subject { AccessToken.new(client, token) } | |
23 | 23 | |
24 | describe "#initialize" do | |
25 | it "assigns client and token" do | |
24 | describe '#initialize' do | |
25 | it 'assigns client and token' do | |
26 | 26 | expect(subject.client).to eq(client) |
27 | 27 | expect(subject.token).to eq(token) |
28 | 28 | end |
29 | 29 | |
30 | it "assigns extra params" do | |
30 | it 'assigns extra params' do | |
31 | 31 | target = AccessToken.new(client, token, 'foo' => 'bar') |
32 | 32 | expect(target.params).to include('foo') |
33 | 33 | expect(target.params['foo']).to eq('bar') |
40 | 40 | expect(target.params['foo']).to eq('bar') |
41 | 41 | end |
42 | 42 | |
43 | it "initializes with a Hash" do | |
43 | it 'initializes with a Hash' do | |
44 | 44 | hash = {:access_token => token, :expires_at => Time.now.to_i + 200, 'foo' => 'bar'} |
45 | 45 | target = AccessToken.from_hash(client, hash) |
46 | 46 | assert_initialized_token(target) |
47 | 47 | end |
48 | 48 | |
49 | it "initalizes with a form-urlencoded key/value string" do | |
50 | kvform = "access_token=#{token}&expires_at=#{Time.now.to_i+200}&foo=bar" | |
49 | it 'initalizes with a form-urlencoded key/value string' do | |
50 | kvform = "access_token=#{token}&expires_at=#{Time.now.to_i + 200}&foo=bar" | |
51 | 51 | target = AccessToken.from_kvform(client, kvform) |
52 | 52 | assert_initialized_token(target) |
53 | 53 | end |
54 | 54 | |
55 | it "sets options" do | |
55 | it 'sets options' do | |
56 | 56 | target = AccessToken.new(client, token, :param_name => 'foo', :header_format => 'Bearer %', :mode => :body) |
57 | 57 | expect(target.options[:param_name]).to eq('foo') |
58 | 58 | expect(target.options[:header_format]).to eq('Bearer %') |
59 | 59 | expect(target.options[:mode]).to eq(:body) |
60 | 60 | end |
61 | ||
62 | it "initializes with a string expires_at" do | |
61 | ||
62 | it 'initializes with a string expires_at' do | |
63 | 63 | hash = {:access_token => token, :expires_at => '1361396829', 'foo' => 'bar'} |
64 | 64 | target = AccessToken.from_hash(client, hash) |
65 | 65 | assert_initialized_token(target) |
67 | 67 | end |
68 | 68 | end |
69 | 69 | |
70 | describe "#request" do | |
71 | context ":mode => :header" do | |
72 | before :all do | |
70 | describe '#request' do | |
71 | context ':mode => :header' do | |
72 | before do | |
73 | 73 | subject.options[:mode] = :header |
74 | 74 | end |
75 | 75 | |
80 | 80 | end |
81 | 81 | end |
82 | 82 | |
83 | context ":mode => :query" do | |
84 | before :all do | |
83 | context ':mode => :query' do | |
84 | before do | |
85 | 85 | subject.options[:mode] = :query |
86 | 86 | end |
87 | 87 | |
92 | 92 | end |
93 | 93 | end |
94 | 94 | |
95 | context ":mode => :body" do | |
96 | before :all do | |
95 | context ':mode => :body' do | |
96 | before do | |
97 | 97 | subject.options[:mode] = :body |
98 | 98 | end |
99 | 99 | |
105 | 105 | end |
106 | 106 | end |
107 | 107 | |
108 | describe "#expires?" do | |
109 | it "is false if there is no expires_at" do | |
108 | describe '#expires?' do | |
109 | it 'is false if there is no expires_at' do | |
110 | 110 | expect(AccessToken.new(client, token)).not_to be_expires |
111 | 111 | end |
112 | 112 | |
113 | it "is true if there is an expires_in" do | |
113 | it 'is true if there is an expires_in' do | |
114 | 114 | expect(AccessToken.new(client, token, :refresh_token => 'abaca', :expires_in => 600)).to be_expires |
115 | 115 | end |
116 | 116 | |
117 | it "is true if there is an expires_at" do | |
118 | expect(AccessToken.new(client, token, :refresh_token => 'abaca', :expires_in => Time.now.getutc.to_i+600)).to be_expires | |
117 | it 'is true if there is an expires_at' do | |
118 | expect(AccessToken.new(client, token, :refresh_token => 'abaca', :expires_in => Time.now.getutc.to_i + 600)).to be_expires | |
119 | 119 | end |
120 | 120 | end |
121 | 121 | |
122 | describe "#expired?" do | |
123 | it "is false if there is no expires_in or expires_at" do | |
122 | describe '#expired?' do | |
123 | it 'is false if there is no expires_in or expires_at' do | |
124 | 124 | expect(AccessToken.new(client, token)).not_to be_expired |
125 | 125 | end |
126 | 126 | |
127 | it "is false if expires_in is in the future" do | |
128 | expect(AccessToken.new(client, token, :refresh_token => 'abaca', :expires_in => 10800)).not_to be_expired | |
127 | it 'is false if expires_in is in the future' do | |
128 | expect(AccessToken.new(client, token, :refresh_token => 'abaca', :expires_in => 10_800)).not_to be_expired | |
129 | 129 | end |
130 | 130 | |
131 | it "is true if expires_at is in the past" do | |
131 | it 'is true if expires_at is in the past' do | |
132 | 132 | access = AccessToken.new(client, token, :refresh_token => 'abaca', :expires_in => 600) |
133 | @now = Time.now + 10800 | |
134 | Time.stub!(:now).and_return(@now) | |
133 | @now = Time.now + 10_800 | |
134 | allow(Time).to receive(:now).and_return(@now) | |
135 | 135 | expect(access).to be_expired |
136 | 136 | end |
137 | 137 | |
138 | 138 | end |
139 | 139 | |
140 | describe "#refresh!" do | |
141 | let(:access) { | |
140 | describe '#refresh!' do | |
141 | let(:access) do | |
142 | 142 | AccessToken.new(client, token, :refresh_token => 'abaca', |
143 | 143 | :expires_in => 600, |
144 | 144 | :param_name => 'o_param') |
145 | } | |
145 | end | |
146 | 146 | |
147 | it "returns a refresh token with appropriate values carried over" do | |
147 | it 'returns a refresh token with appropriate values carried over' do | |
148 | 148 | refreshed = access.refresh! |
149 | 149 | expect(access.client).to eq(refreshed.client) |
150 | 150 | expect(access.options[:param_name]).to eq(refreshed.options[:param_name]) |
151 | 151 | end |
152 | 152 | |
153 | context "with a nil refresh_token in the response" do | |
153 | context 'with a nil refresh_token in the response' do | |
154 | 154 | let(:refresh_body) { MultiJson.encode(:access_token => 'refreshed_foo', :expires_in => 600, :refresh_token => nil) } |
155 | 155 | |
156 | it "copies the refresh_token from the original token" do | |
156 | it 'copies the refresh_token from the original token' do | |
157 | 157 | refreshed = access.refresh! |
158 | 158 | |
159 | 159 | expect(refreshed.refresh_token).to eq(access.refresh_token) |
160 | 160 | end |
161 | 161 | end |
162 | 162 | end |
163 | ||
164 | describe '#to_hash' do | |
165 | it 'return a hash equals to the hash used to initialize access token' do | |
166 | hash = {:access_token => token, :refresh_token => 'foobar', :expires_at => Time.now.to_i + 200, 'foo' => 'bar'} | |
167 | access_token = AccessToken.from_hash(client, hash.clone) | |
168 | expect(access_token.to_hash).to eq(hash) | |
169 | end | |
170 | end | |
163 | 171 | end |
0 | 0 | require 'helper' |
1 | 1 | |
2 | 2 | describe OAuth2::Client do |
3 | let!(:error_value) {'invalid_token'} | |
4 | let!(:error_description_value) {'bad bad token'} | |
3 | let!(:error_value) { 'invalid_token' } | |
4 | let!(:error_description_value) { 'bad bad token' } | |
5 | 5 | |
6 | 6 | subject do |
7 | 7 | OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com') do |builder| |
8 | 8 | builder.adapter :test do |stub| |
9 | stub.get('/success') {|env| [200, {'Content-Type' => 'text/awesome'}, 'yay']} | |
10 | stub.get('/reflect') {|env| [200, {}, env[:body]]} | |
11 | stub.post('/reflect') {|env| [200, {}, env[:body]]} | |
12 | stub.get('/unauthorized') {|env| [401, {'Content-Type' => 'application/json'}, MultiJson.encode(:error => error_value, :error_description => error_description_value)]} | |
13 | stub.get('/conflict') {|env| [409, {'Content-Type' => 'text/plain'}, 'not authorized']} | |
14 | stub.get('/redirect') {|env| [302, {'Content-Type' => 'text/plain', 'location' => '/success' }, '']} | |
15 | stub.post('/redirect') {|env| [303, {'Content-Type' => 'text/plain', 'location' => '/reflect' }, '']} | |
16 | stub.get('/error') {|env| [500, {'Content-Type' => 'text/plain'}, 'unknown error']} | |
17 | stub.get('/empty_get') {|env| [204, {}, nil]} | |
18 | end | |
19 | end | |
20 | end | |
21 | ||
22 | describe "#initialize" do | |
23 | it "assigns id and secret" do | |
9 | stub.get('/success') { |env| [200, {'Content-Type' => 'text/awesome'}, 'yay'] } | |
10 | stub.get('/reflect') { |env| [200, {}, env[:body]] } | |
11 | stub.post('/reflect') { |env| [200, {}, env[:body]] } | |
12 | stub.get('/unauthorized') { |env| [401, {'Content-Type' => 'application/json'}, MultiJson.encode(:error => error_value, :error_description => error_description_value)] } | |
13 | stub.get('/conflict') { |env| [409, {'Content-Type' => 'text/plain'}, 'not authorized'] } | |
14 | stub.get('/redirect') { |env| [302, {'Content-Type' => 'text/plain', 'location' => '/success'}, ''] } | |
15 | stub.post('/redirect') { |env| [303, {'Content-Type' => 'text/plain', 'location' => '/reflect'}, ''] } | |
16 | stub.get('/error') { |env| [500, {'Content-Type' => 'text/plain'}, 'unknown error'] } | |
17 | stub.get('/empty_get') { |env| [204, {}, nil] } | |
18 | end | |
19 | end | |
20 | end | |
21 | ||
22 | describe '#initialize' do | |
23 | it 'assigns id and secret' do | |
24 | 24 | expect(subject.id).to eq('abc') |
25 | 25 | expect(subject.secret).to eq('def') |
26 | 26 | end |
27 | 27 | |
28 | it "assigns site from the options hash" do | |
28 | it 'assigns site from the options hash' do | |
29 | 29 | expect(subject.site).to eq('https://api.example.com') |
30 | 30 | end |
31 | 31 | |
32 | it "assigns Faraday::Connection#host" do | |
32 | it 'assigns Faraday::Connection#host' do | |
33 | 33 | expect(subject.connection.host).to eq('api.example.com') |
34 | 34 | end |
35 | 35 | |
36 | it "leaves Faraday::Connection#ssl unset" do | |
37 | expect(subject.connection.ssl).to eq({}) | |
38 | end | |
39 | ||
40 | it "is able to pass a block to configure the connection" do | |
41 | connection = stub('connection') | |
42 | session = stub('session', :to_ary => nil) | |
43 | builder = stub('builder') | |
44 | connection.stub(:build).and_yield(builder) | |
45 | Faraday::Connection.stub(:new => connection) | |
46 | ||
47 | builder.should_receive(:adapter).with(:test) | |
48 | ||
49 | OAuth2::Client.new('abc', 'def') do |builder| | |
50 | builder.adapter :test | |
36 | it 'leaves Faraday::Connection#ssl unset' do | |
37 | expect(subject.connection.ssl).to be_empty | |
38 | end | |
39 | ||
40 | it 'is able to pass a block to configure the connection' do | |
41 | connection = double('connection') | |
42 | builder = double('builder') | |
43 | allow(connection).to receive(:build).and_yield(builder) | |
44 | allow(Faraday::Connection).to receive(:new).and_return(connection) | |
45 | ||
46 | expect(builder).to receive(:adapter).with(:test) | |
47 | ||
48 | OAuth2::Client.new('abc', 'def') do |client| | |
49 | client.adapter :test | |
51 | 50 | end.connection |
52 | 51 | end |
53 | 52 | |
54 | it "defaults raise_errors to true" do | |
55 | expect(subject.options[:raise_errors]).to be_true | |
56 | end | |
57 | ||
58 | it "allows true/false for raise_errors option" do | |
53 | it 'defaults raise_errors to true' do | |
54 | expect(subject.options[:raise_errors]).to be true | |
55 | end | |
56 | ||
57 | it 'allows true/false for raise_errors option' do | |
59 | 58 | client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :raise_errors => false) |
60 | expect(client.options[:raise_errors]).to be_false | |
59 | expect(client.options[:raise_errors]).to be false | |
61 | 60 | client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :raise_errors => true) |
62 | expect(client.options[:raise_errors]).to be_true | |
63 | end | |
64 | ||
65 | it "allows get/post for access_token_method option" do | |
61 | expect(client.options[:raise_errors]).to be true | |
62 | end | |
63 | ||
64 | it 'allows override of raise_errors option' do | |
65 | client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :raise_errors => true) do |builder| | |
66 | builder.adapter :test do |stub| | |
67 | stub.get('/notfound') { |env| [404, {}, nil] } | |
68 | end | |
69 | end | |
70 | expect(client.options[:raise_errors]).to be true | |
71 | expect { client.request(:get, '/notfound') }.to raise_error(OAuth2::Error) | |
72 | response = client.request(:get, '/notfound', :raise_errors => false) | |
73 | expect(response.status).to eq(404) | |
74 | end | |
75 | ||
76 | it 'allows get/post for access_token_method option' do | |
66 | 77 | client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :access_token_method => :get) |
67 | 78 | expect(client.options[:access_token_method]).to eq(:get) |
68 | 79 | client = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :access_token_method => :post) |
69 | 80 | expect(client.options[:access_token_method]).to eq(:post) |
70 | 81 | end |
82 | ||
83 | it 'does not mutate the opts hash argument' do | |
84 | opts = {:site => 'http://example.com/'} | |
85 | opts2 = opts.dup | |
86 | OAuth2::Client.new 'abc', 'def', opts | |
87 | expect(opts).to eq(opts2) | |
88 | end | |
71 | 89 | end |
72 | 90 | |
73 | 91 | %w(authorize token).each do |url_type| |
81 | 99 | expect(subject.send("#{url_type}_url")).to eq('https://api.example.com/oauth/custom') |
82 | 100 | end |
83 | 101 | |
84 | it "allows a different host than the site" do | |
102 | it 'allows a different host than the site' do | |
85 | 103 | subject.options[:"#{url_type}_url"] = 'https://api.foo.com/oauth/custom' |
86 | 104 | expect(subject.send("#{url_type}_url")).to eq('https://api.foo.com/oauth/custom') |
87 | 105 | end |
88 | 106 | end |
89 | 107 | end |
90 | 108 | |
91 | describe "#request" do | |
92 | it "works with a null response body" do | |
109 | describe '#request' do | |
110 | it 'works with a null response body' do | |
93 | 111 | expect(subject.request(:get, 'empty_get').body).to eq('') |
94 | 112 | end |
95 | 113 | |
96 | it "returns on a successful response" do | |
114 | it 'returns on a successful response' do | |
97 | 115 | response = subject.request(:get, '/success') |
98 | 116 | expect(response.body).to eq('yay') |
99 | 117 | expect(response.status).to eq(200) |
100 | expect(response.headers).to eq({'Content-Type' => 'text/awesome'}) | |
101 | end | |
102 | ||
103 | it "posts a body" do | |
118 | expect(response.headers).to eq('Content-Type' => 'text/awesome') | |
119 | end | |
120 | ||
121 | it 'posts a body' do | |
104 | 122 | response = subject.request(:post, '/reflect', :body => 'foo=bar') |
105 | 123 | expect(response.body).to eq('foo=bar') |
106 | 124 | end |
107 | 125 | |
108 | it "follows redirects properly" do | |
126 | it 'follows redirects properly' do | |
109 | 127 | response = subject.request(:get, '/redirect') |
110 | 128 | expect(response.body).to eq('yay') |
111 | 129 | expect(response.status).to eq(200) |
112 | expect(response.headers).to eq({'Content-Type' => 'text/awesome'}) | |
113 | end | |
114 | ||
115 | it "redirects using GET on a 303" do | |
130 | expect(response.headers).to eq('Content-Type' => 'text/awesome') | |
131 | end | |
132 | ||
133 | it 'redirects using GET on a 303' do | |
116 | 134 | response = subject.request(:post, '/redirect', :body => 'foo=bar') |
117 | 135 | expect(response.body).to be_empty |
118 | 136 | expect(response.status).to eq(200) |
119 | 137 | end |
120 | 138 | |
121 | it "obeys the :max_redirects option" do | |
139 | it 'obeys the :max_redirects option' do | |
122 | 140 | max_redirects = subject.options[:max_redirects] |
123 | 141 | subject.options[:max_redirects] = 0 |
124 | 142 | response = subject.request(:get, '/redirect') |
126 | 144 | subject.options[:max_redirects] = max_redirects |
127 | 145 | end |
128 | 146 | |
129 | it "returns if raise_errors is false" do | |
147 | it 'returns if raise_errors is false' do | |
130 | 148 | subject.options[:raise_errors] = false |
131 | 149 | response = subject.request(:get, '/unauthorized') |
132 | 150 | |
133 | 151 | expect(response.status).to eq(401) |
134 | expect(response.headers).to eq({'Content-Type' => 'application/json'}) | |
152 | expect(response.headers).to eq('Content-Type' => 'application/json') | |
135 | 153 | expect(response.error).not_to be_nil |
136 | 154 | end |
137 | 155 | |
138 | 156 | %w(/unauthorized /conflict /error).each do |error_path| |
139 | 157 | it "raises OAuth2::Error on error response to path #{error_path}" do |
140 | expect{subject.request(:get, error_path)}.to raise_error(OAuth2::Error) | |
141 | end | |
142 | end | |
143 | ||
144 | it "parses OAuth2 standard error response" do | |
158 | expect { subject.request(:get, error_path) }.to raise_error(OAuth2::Error) | |
159 | end | |
160 | end | |
161 | ||
162 | it 'parses OAuth2 standard error response' do | |
145 | 163 | begin |
146 | 164 | subject.request(:get, '/unauthorized') |
147 | rescue Exception => e | |
165 | rescue StandardError => e | |
148 | 166 | expect(e.code).to eq(error_value) |
149 | 167 | expect(e.description).to eq(error_description_value) |
150 | 168 | expect(e.to_s).to match(/#{error_value}/) |
152 | 170 | end |
153 | 171 | end |
154 | 172 | |
155 | it "provides the response in the Exception" do | |
173 | it 'provides the response in the Exception' do | |
156 | 174 | begin |
157 | 175 | subject.request(:get, '/error') |
158 | rescue Exception => e | |
176 | rescue StandardError => e | |
159 | 177 | expect(e.response).not_to be_nil |
160 | 178 | expect(e.to_s).to match(/unknown error/) |
161 | 179 | end |
162 | 180 | end |
163 | 181 | end |
164 | 182 | |
165 | it "instantiates an AuthCode strategy with this client" do | |
183 | it 'instantiates an AuthCode strategy with this client' do | |
166 | 184 | expect(subject.auth_code).to be_kind_of(OAuth2::Strategy::AuthCode) |
167 | 185 | end |
168 | 186 | |
169 | it "instantiates an Implicit strategy with this client" do | |
187 | it 'instantiates an Implicit strategy with this client' do | |
170 | 188 | expect(subject.implicit).to be_kind_of(OAuth2::Strategy::Implicit) |
171 | 189 | end |
172 | 190 | |
173 | context "with SSL options" do | |
191 | context 'with SSL options' do | |
174 | 192 | subject do |
175 | 193 | cli = OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com', :ssl => {:ca_file => 'foo.pem'}) |
176 | 194 | cli.connection.build do |b| |
179 | 197 | cli |
180 | 198 | end |
181 | 199 | |
182 | it "passes the SSL options along to Faraday::Connection#ssl" do | |
183 | expect(subject.connection.ssl).to eq({:ca_file => 'foo.pem'}) | |
200 | it 'passes the SSL options along to Faraday::Connection#ssl' do | |
201 | expect(subject.connection.ssl.fetch(:ca_file)).to eq('foo.pem') | |
184 | 202 | end |
185 | 203 | end |
186 | 204 | end |
0 | 0 | require 'helper' |
1 | 1 | |
2 | 2 | describe OAuth2::Response do |
3 | describe "#initialize" do | |
4 | let(:status) {200} | |
5 | let(:headers) {{'foo' => 'bar'}} | |
6 | let(:body) {'foo'} | |
3 | describe '#initialize' do | |
4 | let(:status) { 200 } | |
5 | let(:headers) { {'foo' => 'bar'} } | |
6 | let(:body) { 'foo' } | |
7 | 7 | |
8 | it "returns the status, headers and body" do | |
8 | it 'returns the status, headers and body' do | |
9 | 9 | response = double('response', :headers => headers, |
10 | 10 | :status => status, |
11 | 11 | :body => body) |
16 | 16 | end |
17 | 17 | end |
18 | 18 | |
19 | describe ".register_parser" do | |
20 | let(:response) { | |
19 | describe '.register_parser' do | |
20 | let(:response) do | |
21 | 21 | double('response', :headers => {'Content-Type' => 'application/foo-bar'}, |
22 | 22 | :status => 200, |
23 | 23 | :body => 'baz') |
24 | } | |
24 | end | |
25 | 25 | before do |
26 | 26 | OAuth2::Response.register_parser(:foobar, 'application/foo-bar') do |body| |
27 | 27 | "foobar #{body}" |
28 | 28 | end |
29 | 29 | end |
30 | 30 | |
31 | it "adds to the content types and parsers" do | |
31 | it 'adds to the content types and parsers' do | |
32 | 32 | expect(OAuth2::Response::PARSERS.keys).to include(:foobar) |
33 | 33 | expect(OAuth2::Response::CONTENT_TYPES.keys).to include('application/foo-bar') |
34 | 34 | end |
35 | 35 | |
36 | it "is able to parse that content type automatically" do | |
36 | it 'is able to parse that content type automatically' do | |
37 | 37 | expect(OAuth2::Response.new(response).parsed).to eq('foobar baz') |
38 | 38 | end |
39 | 39 | end |
40 | 40 | |
41 | describe "#parsed" do | |
42 | it "parses application/x-www-form-urlencoded body" do | |
41 | describe '#parsed' do | |
42 | it 'parses application/x-www-form-urlencoded body' do | |
43 | 43 | headers = {'Content-Type' => 'application/x-www-form-urlencoded'} |
44 | 44 | body = 'foo=bar&answer=42' |
45 | 45 | response = double('response', :headers => headers, :body => body) |
49 | 49 | expect(subject.parsed['answer']).to eq('42') |
50 | 50 | end |
51 | 51 | |
52 | it "parses application/json body" do | |
52 | it 'parses application/json body' do | |
53 | 53 | headers = {'Content-Type' => 'application/json'} |
54 | 54 | body = MultiJson.encode(:foo => 'bar', :answer => 42) |
55 | 55 | response = double('response', :headers => headers, :body => body) |
65 | 65 | |
66 | 66 | response = double('response', :headers => headers, :body => body) |
67 | 67 | |
68 | MultiJson.should_not_receive(:decode) | |
69 | MultiJson.should_not_receive(:load) | |
70 | Rack::Utils.should_not_receive(:parse_query) | |
68 | expect(MultiJson).not_to receive(:decode) | |
69 | expect(MultiJson).not_to receive(:load) | |
70 | expect(Rack::Utils).not_to receive(:parse_query) | |
71 | 71 | |
72 | 72 | subject = Response.new(response) |
73 | 73 | expect(subject.parsed).to be_nil |
74 | 74 | end |
75 | 75 | end |
76 | 76 | |
77 | context "xml parser registration" do | |
78 | it "tries to load multi_xml and use it" do | |
77 | context 'xml parser registration' do | |
78 | it 'tries to load multi_xml and use it' do | |
79 | 79 | expect(OAuth2::Response::PARSERS[:xml]).not_to be_nil |
80 | 80 | end |
81 | 81 | |
82 | it "is able to parse xml" do | |
82 | it 'is able to parse xml' do | |
83 | 83 | headers = {'Content-Type' => 'text/xml'} |
84 | 84 | body = '<?xml version="1.0" standalone="yes" ?><foo><bar>baz</bar></foo>' |
85 | 85 | |
86 | 86 | response = double('response', :headers => headers, :body => body) |
87 | expect(OAuth2::Response.new(response).parsed).to eq({"foo" => {"bar" => "baz"}}) | |
87 | expect(OAuth2::Response.new(response).parsed).to eq('foo' => {'bar' => 'baz'}) | |
88 | 88 | end |
89 | 89 | end |
90 | 90 | end |
6 | 6 | b.adapter :test do |stub| |
7 | 7 | stub.post('/oauth/token') do |env| |
8 | 8 | case @mode |
9 | when "formencoded" | |
10 | [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, 'expires_in=600&access_token=salmon&refresh_token=trout'] | |
11 | when "json" | |
12 | [200, {'Content-Type' => 'application/json'}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}'] | |
9 | when 'formencoded' | |
10 | [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, 'expires_in=600&access_token=salmon&refresh_token=trout'] | |
11 | when 'json' | |
12 | [200, {'Content-Type' => 'application/json'}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}'] | |
13 | 13 | end |
14 | 14 | end |
15 | 15 | end |
17 | 17 | cli |
18 | 18 | end |
19 | 19 | |
20 | let(:params) { {:hmac_secret => 'foo'}} | |
20 | let(:params) { {:hmac_secret => 'foo'} } | |
21 | 21 | |
22 | subject {client.assertion} | |
22 | subject { client.assertion } | |
23 | 23 | |
24 | describe "#authorize_url" do | |
25 | it "raises NotImplementedError" do | |
26 | expect{subject.authorize_url}.to raise_error(NotImplementedError) | |
24 | describe '#authorize_url' do | |
25 | it 'raises NotImplementedError' do | |
26 | expect { subject.authorize_url }.to raise_error(NotImplementedError) | |
27 | 27 | end |
28 | 28 | end |
29 | 29 | |
34 | 34 | @access = subject.get_token(params) |
35 | 35 | end |
36 | 36 | |
37 | it "returns AccessToken with same Client" do | |
37 | it 'returns AccessToken with same Client' do | |
38 | 38 | expect(@access.client).to eq(client) |
39 | 39 | end |
40 | 40 | |
41 | it "returns AccessToken with #token" do | |
41 | it 'returns AccessToken with #token' do | |
42 | 42 | expect(@access.token).to eq('salmon') |
43 | 43 | end |
44 | 44 | |
45 | it "returns AccessToken with #expires_in" do | |
45 | it 'returns AccessToken with #expires_in' do | |
46 | 46 | expect(@access.expires_in).to eq(600) |
47 | 47 | end |
48 | 48 | |
49 | it "returns AccessToken with #expires_at" do | |
49 | it 'returns AccessToken with #expires_at' do | |
50 | 50 | expect(@access.expires_at).not_to be_nil |
51 | 51 | end |
52 | 52 | end |
53 | 53 | end |
54 | 54 | |
55 | 55 | end |
56 |
0 | 0 | require 'helper' |
1 | 1 | |
2 | 2 | describe OAuth2::Strategy::AuthCode do |
3 | let(:code) {'sushi'} | |
4 | let(:kvform_token) {'expires_in=600&access_token=salmon&refresh_token=trout&extra_param=steve'} | |
5 | let(:facebook_token) {kvform_token.gsub('_in', '')} | |
6 | let(:json_token) {MultiJson.encode(:expires_in => 600, :access_token => 'salmon', :refresh_token => 'trout', :extra_param => 'steve')} | |
3 | let(:code) { 'sushi' } | |
4 | let(:kvform_token) { 'expires_in=600&access_token=salmon&refresh_token=trout&extra_param=steve' } | |
5 | let(:facebook_token) { kvform_token.gsub('_in', '') } | |
6 | let(:json_token) { MultiJson.encode(:expires_in => 600, :access_token => 'salmon', :refresh_token => 'trout', :extra_param => 'steve') } | |
7 | 7 | |
8 | 8 | let(:client) do |
9 | 9 | OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com') do |builder| |
10 | 10 | builder.adapter :test do |stub| |
11 | 11 | stub.get("/oauth/token?client_id=abc&client_secret=def&code=#{code}&grant_type=authorization_code") do |env| |
12 | 12 | case @mode |
13 | when "formencoded" | |
13 | when 'formencoded' | |
14 | 14 | [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token] |
15 | when "json" | |
15 | when 'json' | |
16 | 16 | [200, {'Content-Type' => 'application/json'}, json_token] |
17 | when "from_facebook" | |
17 | when 'from_facebook' | |
18 | 18 | [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, facebook_token] |
19 | 19 | end |
20 | 20 | end |
21 | stub.post('/oauth/token', {'client_id' => 'abc', 'client_secret' => 'def', 'code' => 'sushi', 'grant_type' => 'authorization_code'}) do |env| | |
21 | stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def', 'code' => 'sushi', 'grant_type' => 'authorization_code') do |env| | |
22 | 22 | case @mode |
23 | when "formencoded" | |
23 | when 'formencoded' | |
24 | 24 | [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token] |
25 | when "json" | |
25 | when 'json' | |
26 | 26 | [200, {'Content-Type' => 'application/json'}, json_token] |
27 | when "from_facebook" | |
27 | when 'from_facebook' | |
28 | 28 | [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, facebook_token] |
29 | 29 | end |
30 | 30 | end |
32 | 32 | end |
33 | 33 | end |
34 | 34 | |
35 | subject {client.auth_code} | |
35 | subject { client.auth_code } | |
36 | 36 | |
37 | describe "#authorize_url" do | |
38 | it "includes the client_id" do | |
37 | describe '#authorize_url' do | |
38 | it 'includes the client_id' do | |
39 | 39 | expect(subject.authorize_url).to include('client_id=abc') |
40 | 40 | end |
41 | 41 | |
42 | it "includes the type" do | |
42 | it 'includes the type' do | |
43 | 43 | expect(subject.authorize_url).to include('response_type=code') |
44 | 44 | end |
45 | 45 | |
46 | it "includes passed in options" do | |
46 | it 'includes passed in options' do | |
47 | 47 | cb = 'http://myserver.local/oauth/callback' |
48 | 48 | expect(subject.authorize_url(:redirect_uri => cb)).to include("redirect_uri=#{Rack::Utils.escape(cb)}") |
49 | 49 | end |
52 | 52 | %w(json formencoded from_facebook).each do |mode| |
53 | 53 | [:get, :post].each do |verb| |
54 | 54 | describe "#get_token (#{mode}, access_token_method=#{verb}" do |
55 | before :each do | |
55 | before do | |
56 | 56 | @mode = mode |
57 | 57 | client.options[:token_method] = verb |
58 | 58 | @access = subject.get_token(code) |
59 | 59 | end |
60 | 60 | |
61 | it "returns AccessToken with same Client" do | |
61 | it 'returns AccessToken with same Client' do | |
62 | 62 | expect(@access.client).to eq(client) |
63 | 63 | end |
64 | 64 | |
65 | it "returns AccessToken with #token" do | |
65 | it 'returns AccessToken with #token' do | |
66 | 66 | expect(@access.token).to eq('salmon') |
67 | 67 | end |
68 | 68 | |
69 | it "returns AccessToken with #refresh_token" do | |
69 | it 'returns AccessToken with #refresh_token' do | |
70 | 70 | expect(@access.refresh_token).to eq('trout') |
71 | 71 | end |
72 | 72 | |
73 | it "returns AccessToken with #expires_in" do | |
73 | it 'returns AccessToken with #expires_in' do | |
74 | 74 | expect(@access.expires_in).to eq(600) |
75 | 75 | end |
76 | 76 | |
77 | it "returns AccessToken with #expires_at" do | |
77 | it 'returns AccessToken with #expires_at' do | |
78 | 78 | expect(@access.expires_at).to be_kind_of(Integer) |
79 | 79 | end |
80 | 80 | |
81 | it "returns AccessToken with params accessible via []" do | |
81 | it 'returns AccessToken with params accessible via []' do | |
82 | 82 | expect(@access['extra_param']).to eq('steve') |
83 | 83 | end |
84 | 84 | end |
0 | 0 | require 'helper' |
1 | 1 | |
2 | 2 | describe OAuth2::Strategy::Base do |
3 | it "initializes with a Client" do | |
4 | expect{OAuth2::Strategy::Base.new(OAuth2::Client.new('abc', 'def'))}.not_to raise_error | |
3 | it 'initializes with a Client' do | |
4 | expect { OAuth2::Strategy::Base.new(OAuth2::Client.new('abc', 'def')) }.not_to raise_error | |
5 | 5 | end |
6 | 6 | end |
6 | 6 | let(:client) do |
7 | 7 | OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com') do |builder| |
8 | 8 | builder.adapter :test do |stub| |
9 | stub.post('/oauth/token', {'grant_type' => 'client_credentials'}) do |env| | |
10 | client_id, client_secret = HTTPAuth::Basic.unpack_authorization(env[:request_headers]['Authorization']) | |
11 | client_id == 'abc' && client_secret == 'def' or raise Faraday::Adapter::Test::Stubs::NotFound.new | |
9 | stub.post('/oauth/token', 'grant_type' => 'client_credentials') do |env| | |
10 | client_id, client_secret = Base64.decode64(env[:request_headers]['Authorization'].split(' ', 2)[1]).split(':', 2) | |
11 | client_id == 'abc' && client_secret == 'def' || fail(Faraday::Adapter::Test::Stubs::NotFound) | |
12 | 12 | case @mode |
13 | when "formencoded" | |
13 | when 'formencoded' | |
14 | 14 | [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token] |
15 | when "json" | |
15 | when 'json' | |
16 | 16 | [200, {'Content-Type' => 'application/json'}, json_token] |
17 | 17 | end |
18 | 18 | end |
19 | stub.post('/oauth/token', {'client_id' => 'abc', 'client_secret' => 'def', 'grant_type' => 'client_credentials'}) do |env| | |
19 | stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def', 'grant_type' => 'client_credentials') do |env| | |
20 | 20 | case @mode |
21 | when "formencoded" | |
21 | when 'formencoded' | |
22 | 22 | [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token] |
23 | when "json" | |
23 | when 'json' | |
24 | 24 | [200, {'Content-Type' => 'application/json'}, json_token] |
25 | 25 | end |
26 | 26 | end |
28 | 28 | end |
29 | 29 | end |
30 | 30 | |
31 | subject {client.client_credentials} | |
31 | subject { client.client_credentials } | |
32 | 32 | |
33 | describe "#authorize_url" do | |
34 | it "raises NotImplementedError" do | |
35 | expect{subject.authorize_url}.to raise_error(NotImplementedError) | |
33 | describe '#authorize_url' do | |
34 | it 'raises NotImplementedError' do | |
35 | expect { subject.authorize_url }.to raise_error(NotImplementedError) | |
36 | end | |
37 | end | |
38 | ||
39 | describe '#authorization' do | |
40 | it 'generates an Authorization header value for HTTP Basic Authentication' do | |
41 | [ | |
42 | ['abc', 'def', 'Basic YWJjOmRlZg=='], | |
43 | ['xxx', 'secret', 'Basic eHh4OnNlY3JldA=='] | |
44 | ].each do |client_id, client_secret, expected| | |
45 | expect(subject.authorization(client_id, client_secret)).to eq(expected) | |
46 | end | |
36 | 47 | end |
37 | 48 | end |
38 | 49 | |
44 | 55 | @access = subject.get_token({}, auth_scheme == 'default' ? {} : {'auth_scheme' => auth_scheme}) |
45 | 56 | end |
46 | 57 | |
47 | it "returns AccessToken with same Client" do | |
58 | it 'returns AccessToken with same Client' do | |
48 | 59 | expect(@access.client).to eq(client) |
49 | 60 | end |
50 | 61 | |
51 | it "returns AccessToken with #token" do | |
62 | it 'returns AccessToken with #token' do | |
52 | 63 | expect(@access.token).to eq('salmon') |
53 | 64 | end |
54 | 65 | |
55 | it "returns AccessToken without #refresh_token" do | |
66 | it 'returns AccessToken without #refresh_token' do | |
56 | 67 | expect(@access.refresh_token).to be_nil |
57 | 68 | end |
58 | 69 | |
59 | it "returns AccessToken with #expires_in" do | |
70 | it 'returns AccessToken with #expires_in' do | |
60 | 71 | expect(@access.expires_in).to eq(600) |
61 | 72 | end |
62 | 73 | |
63 | it "returns AccessToken with #expires_at" do | |
74 | it 'returns AccessToken with #expires_at' do | |
64 | 75 | expect(@access.expires_at).not_to be_nil |
65 | 76 | end |
66 | 77 | end |
2 | 2 | describe OAuth2::Strategy::Implicit do |
3 | 3 | let(:client) { OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com') } |
4 | 4 | |
5 | subject {client.implicit} | |
5 | subject { client.implicit } | |
6 | 6 | |
7 | describe "#authorize_url" do | |
8 | it "includes the client_id" do | |
7 | describe '#authorize_url' do | |
8 | it 'includes the client_id' do | |
9 | 9 | expect(subject.authorize_url).to include('client_id=abc') |
10 | 10 | end |
11 | 11 | |
12 | it "includes the type" do | |
12 | it 'includes the type' do | |
13 | 13 | expect(subject.authorize_url).to include('response_type=token') |
14 | 14 | end |
15 | 15 | |
16 | it "includes passed in options" do | |
16 | it 'includes passed in options' do | |
17 | 17 | cb = 'http://myserver.local/oauth/callback' |
18 | 18 | expect(subject.authorize_url(:redirect_uri => cb)).to include("redirect_uri=#{Rack::Utils.escape(cb)}") |
19 | 19 | end |
20 | 20 | end |
21 | 21 | |
22 | describe "#get_token" do | |
23 | it "raises NotImplementedError" do | |
24 | expect{subject.get_token}.to raise_error(NotImplementedError) | |
22 | describe '#get_token' do | |
23 | it 'raises NotImplementedError' do | |
24 | expect { subject.get_token }.to raise_error(NotImplementedError) | |
25 | 25 | end |
26 | 26 | end |
27 | 27 | end |
6 | 6 | b.adapter :test do |stub| |
7 | 7 | stub.post('/oauth/token') do |env| |
8 | 8 | case @mode |
9 | when "formencoded" | |
10 | [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, 'expires_in=600&access_token=salmon&refresh_token=trout'] | |
11 | when "json" | |
12 | [200, {'Content-Type' => 'application/json'}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}'] | |
9 | when 'formencoded' | |
10 | [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, 'expires_in=600&access_token=salmon&refresh_token=trout'] | |
11 | when 'json' | |
12 | [200, {'Content-Type' => 'application/json'}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}'] | |
13 | 13 | end |
14 | 14 | end |
15 | 15 | end |
16 | 16 | end |
17 | 17 | cli |
18 | 18 | end |
19 | subject {client.password} | |
19 | subject { client.password } | |
20 | 20 | |
21 | describe "#authorize_url" do | |
22 | it "raises NotImplementedError" do | |
23 | expect{subject.authorize_url}.to raise_error(NotImplementedError) | |
21 | describe '#authorize_url' do | |
22 | it 'raises NotImplementedError' do | |
23 | expect { subject.authorize_url }.to raise_error(NotImplementedError) | |
24 | 24 | end |
25 | 25 | end |
26 | 26 | |
31 | 31 | @access = subject.get_token('username', 'password') |
32 | 32 | end |
33 | 33 | |
34 | it "returns AccessToken with same Client" do | |
34 | it 'returns AccessToken with same Client' do | |
35 | 35 | expect(@access.client).to eq(client) |
36 | 36 | end |
37 | 37 | |
38 | it "returns AccessToken with #token" do | |
38 | it 'returns AccessToken with #token' do | |
39 | 39 | expect(@access.token).to eq('salmon') |
40 | 40 | end |
41 | 41 | |
42 | it "returns AccessToken with #refresh_token" do | |
42 | it 'returns AccessToken with #refresh_token' do | |
43 | 43 | expect(@access.refresh_token).to eq('trout') |
44 | 44 | end |
45 | 45 | |
46 | it "returns AccessToken with #expires_in" do | |
46 | it 'returns AccessToken with #expires_in' do | |
47 | 47 | expect(@access.expires_in).to eq(600) |
48 | 48 | end |
49 | 49 | |
50 | it "returns AccessToken with #expires_at" do | |
50 | it 'returns AccessToken with #expires_at' do | |
51 | 51 | expect(@access.expires_at).not_to be_nil |
52 | 52 | end |
53 | 53 | end |