dreamwords-oauth2 0.8.1

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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gemtest ADDED
File without changes
data/.gitignore ADDED
@@ -0,0 +1,35 @@
1
+ .rvmrc
2
+ /live
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ doc
19
+ rdoc
20
+ log
21
+
22
+ ## BUNDLER
23
+ *.gem
24
+ .bundle
25
+ pkg
26
+ Gemfile.lock
27
+
28
+ ## RCOV
29
+ coverage.data
30
+
31
+ ## PROJECT::SPECIFIC
32
+ .svn
33
+
34
+ ## Rubinius
35
+ *.rbc
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --order random
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ matrix:
3
+ allow_failures:
4
+ - rvm: jruby-18mode
5
+ rvm:
6
+ - rbx-18mode
7
+ - rbx-19mode
8
+ - jruby-18mode
9
+ - jruby-19mode
10
+ - 1.8.7
11
+ - 1.9.2
12
+ - 1.9.3
13
+ - ruby-head
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Intridea, Inc. and Michael Bleigh
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # OAuth2 [![Build Status](https://secure.travis-ci.org/intridea/oauth2.png?branch=master)][travis] [![Dependency Status](https://gemnasium.com/intridea/oauth2.png?travis)][gemnasium]
2
+ A Ruby wrapper for the OAuth 2.0 specification. This is a work in progress,
3
+ being built first to solve the pragmatic process of connecting to existing
4
+ OAuth 2.0 endpoints (a.k.a. Facebook) with the goal of building it up to meet
5
+ the entire specification over time.
6
+
7
+ [travis]: http://travis-ci.org/intridea/oauth2
8
+ [gemnasium]: https://gemnasium.com/intridea/oauth2
9
+
10
+ ## Installation
11
+ gem install oauth2
12
+
13
+ ## Resources
14
+ * [View Source on GitHub][code]
15
+ * [Report Issues on GitHub][issues]
16
+ * [Read More at the Wiki][wiki]
17
+
18
+ [code]: https://github.com/intridea/oauth2
19
+ [issues]: https://github.com/intridea/oauth2/issues
20
+ [wiki]: https://wiki.github.com/intridea/oauth2
21
+
22
+ ## Usage Examples
23
+ require 'oauth2'
24
+ client = OAuth2::Client.new('client_id', 'client_secret', :site => 'https://example.org')
25
+
26
+ client.auth_code.authorize_url(:redirect_uri => 'http://localhost:8080/oauth2/callback')
27
+ # => "https://example.org/oauth/authorization?response_type=code&client_id=client_id&redirect_uri=http://localhost:8080/oauth2/callback"
28
+
29
+ token = client.auth_code.get_token('authorization_code_value', :redirect_uri => 'http://localhost:8080/oauth2/callback', :headers => {'Authorization' => 'Basic some_password'})
30
+ response = token.get('/api/resource', :params => { 'query_foo' => 'bar' })
31
+ response.class.name
32
+ # => OAuth2::Response
33
+
34
+ ## OAuth2::Response
35
+ The AccessToken methods #get, #post, #put and #delete and the generic #request
36
+ will return an instance of the #OAuth2::Response class.
37
+
38
+ This instance contains a #parsed method that will parse the response body and
39
+ return a Hash if the Content-Type is application/x-www-form-urlencoded or if
40
+ the body is a JSON object. It will return an Array if the body is a JSON
41
+ array. Otherwise, it will return the original body string.
42
+
43
+ The original response body, headers, and status can be accessed via their
44
+ respective methods.
45
+
46
+ ## OAuth2::AccessToken
47
+ If you have an existing Access Token for a user, you can initialize an instance
48
+ using various class methods including the standard new, from_hash (if you have
49
+ a hash of the values), or from_kvform (if you have an
50
+ application/x-www-form-urlencoded encoded string of the values).
51
+
52
+ ## OAuth2::Error
53
+ On 400+ status code responses, an OAuth2::Error will be raised. If it is a
54
+ standard OAuth2 error response, the body will be parsed and #code and #description will contain the values provided from the error and
55
+ error_description parameters. The #response property of OAuth2::Error will
56
+ always contain the OAuth2::Response instance.
57
+
58
+ If you do not want an error to be raised, you may use :raise_errors => false
59
+ option on initialization of the client. In this case the OAuth2::Response
60
+ instance will be returned as usual and on 400+ status code responses, the
61
+ Response instance will contain the OAuth2::Error instance.
62
+
63
+ ## Authorization Grants
64
+ Currently the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion
65
+ authentication grant types have helper strategy classes that simplify client
66
+ use. They are available via the #auth_code, #implicit, #password, #client_credentials, and #assertion methods respectively.
67
+
68
+ auth_url = client.auth_code.authorize_url(:redirect_uri => 'http://localhost:8080/oauth/callback')
69
+ token = client.auth_code.get_token('code_value', :redirect_uri => 'http://localhost:8080/oauth/callback')
70
+
71
+ auth_url = client.implicit.authorize_url(:redirect_uri => 'http://localhost:8080/oauth/callback')
72
+ # get the token params in the callback and
73
+ token = OAuth2::AccessToken.from_kvform(client, query_string)
74
+
75
+ token = client.password.get_token('username', 'password')
76
+
77
+ token = client.client_credentials.get_token
78
+
79
+ token = client.assertion.get_token(assertion_params)
80
+
81
+ If you want to specify additional headers to be sent out with the
82
+ request, add a 'headers' hash under 'params':
83
+
84
+ token = client.auth_code.get_token('code_value', :redirect_uri => 'http://localhost:8080/oauth/callback', :headers => {'Some' => 'Header'})
85
+
86
+ You can always use the #request method on the OAuth2::Client instance to make
87
+ requests for tokens for any Authentication grant type.
88
+
89
+ ## Submitting a Pull Request
90
+ 1. [Fork the repository.][fork]
91
+ 2. [Create a topic branch.][branch]
92
+ 3. Add specs for your unimplemented feature or bug fix.
93
+ 4. Run `bundle exec rake spec`. If your specs pass, return to step 3.
94
+ 5. Implement your feature or bug fix.
95
+ 6. Run `bundle exec rake spec`. If your specs fail, return to step 5.
96
+ 7. Run `open coverage/index.html`. If your changes are not completely covered
97
+ by your tests, return to step 3.
98
+ 8. Add, commit, and push your changes.
99
+ 9. [Submit a pull request.][pr]
100
+
101
+ [fork]: http://help.github.com/fork-a-repo/
102
+ [branch]: http://learn.github.com/p/branching.html
103
+ [pr]: http://help.github.com/send-pull-requests/
104
+
105
+ ## Supported Ruby Versions
106
+ This library aims to support and is [tested against][travis] the following Ruby
107
+ implementations:
108
+
109
+ * Ruby 1.8.7
110
+ * Ruby 1.9.2
111
+ * Ruby 1.9.3
112
+ * [JRuby][]
113
+ * [Rubinius][]
114
+
115
+ [jruby]: http://www.jruby.org/
116
+ [rubinius]: http://rubini.us/
117
+
118
+ If something doesn't work on one of these interpreters, it should be considered
119
+ a bug.
120
+
121
+ This library may inadvertently work (or seem to work) on other Ruby
122
+ implementations, however support will only be provided for the versions listed
123
+ above.
124
+
125
+ If you would like this library to support another Ruby version, you may
126
+ volunteer to be a maintainer. Being a maintainer entails making sure all tests
127
+ run and pass on that implementation. When something breaks on your
128
+ implementation, you will be personally responsible for providing patches in a
129
+ timely fashion. If critical issues for a particular implementation exist at the
130
+ time of a major release, support for that Ruby version may be dropped.
131
+
132
+ ## Copyright
133
+ Copyright (c) 2011 Intridea, Inc. and Michael Bleigh.
134
+ See [LICENSE][] for details.
135
+ [license]: https://github.com/intridea/oauth2/blob/master/LICENSE.md
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
8
+ task :test => :spec
9
+
10
+ namespace :doc do
11
+ require 'rdoc/task'
12
+ require File.expand_path('../lib/oauth2/version', __FILE__)
13
+ RDoc::Task.new do |rdoc|
14
+ rdoc.rdoc_dir = 'rdoc'
15
+ rdoc.title = "oauth2 #{OAuth2::Version}"
16
+ rdoc.main = 'README.md'
17
+ rdoc.rdoc_files.include('README.md', 'LICENSE.md', 'lib/**/*.rb')
18
+ end
19
+ end
data/lib/oauth2.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'oauth2/error'
2
+ require 'oauth2/client'
3
+ require 'oauth2/strategy/base'
4
+ require 'oauth2/strategy/auth_code'
5
+ require 'oauth2/strategy/implicit'
6
+ require 'oauth2/strategy/password'
7
+ require 'oauth2/strategy/client_credentials'
8
+ require 'oauth2/strategy/assertion'
9
+ require 'oauth2/access_token'
10
+ require 'oauth2/response'
@@ -0,0 +1,157 @@
1
+ module OAuth2
2
+ class AccessToken
3
+ attr_reader :client, :token, :refresh_token, :expires_in, :expires_at, :params
4
+ attr_accessor :options
5
+
6
+ class << self
7
+ # Initializes an AccessToken from a Hash
8
+ #
9
+ # @param [Client] the OAuth2::Client instance
10
+ # @param [Hash] a hash of AccessToken property values
11
+ # @return [AccessToken] the initalized AccessToken
12
+ def from_hash(client, hash)
13
+ self.new(client, hash.delete('access_token') || hash.delete(:access_token), hash)
14
+ end
15
+
16
+ # Initializes an AccessToken from a key/value application/x-www-form-urlencoded string
17
+ #
18
+ # @param [Client] client the OAuth2::Client instance
19
+ # @param [String] kvform the application/x-www-form-urlencoded string
20
+ # @return [AccessToken] the initalized AccessToken
21
+ def from_kvform(client, kvform)
22
+ from_hash(client, Rack::Utils.parse_query(kvform))
23
+ end
24
+ end
25
+
26
+ # Initalize an AccessToken
27
+ #
28
+ # @param [Client] client the OAuth2::Client instance
29
+ # @param [String] token the Access Token value
30
+ # @param [Hash] opts the options to create the Access Token with
31
+ # @option opts [String] :refresh_token (nil) the refresh_token value
32
+ # @option opts [FixNum, String] :expires_in (nil) the number of seconds in which the AccessToken will expire
33
+ # @option opts [FixNum, String] :expires_at (nil) the epoch time in seconds in which AccessToken will expire
34
+ # @option opts [Symbol] :mode (:header) the transmission mode of the Access Token parameter value
35
+ # one of :header, :body or :query
36
+ # @option opts [String] :header_format ('Bearer %s') the string format to use for the Authorization header
37
+ # @option opts [String] :param_name ('bearer_token') the parameter name to use for transmission of the
38
+ # Access Token value in :body or :query transmission mode
39
+ def initialize(client, token, opts={})
40
+ @client = client
41
+ @token = token.to_s
42
+ [:refresh_token, :expires_in, :expires_at].each do |arg|
43
+ instance_variable_set("@#{arg}", opts.delete(arg) || opts.delete(arg.to_s))
44
+ end
45
+ @expires_in ||= opts.delete('expires')
46
+ @expires_in &&= @expires_in.to_i
47
+ @expires_at &&= @expires_at.to_i
48
+ @expires_at ||= Time.now.to_i + @expires_in if @expires_in
49
+ @options = {:mode => opts.delete(:mode) || :header,
50
+ :header_format => opts.delete(:header_format) || 'Bearer %s',
51
+ :param_name => opts.delete(:param_name) || 'bearer_token'}
52
+ @params = opts
53
+ end
54
+
55
+ # Indexer to additional params present in token response
56
+ #
57
+ # @param [String] key entry key to Hash
58
+ def [](key)
59
+ @params[key]
60
+ end
61
+
62
+ # Whether or not the token expires
63
+ #
64
+ # @return [Boolean]
65
+ def expires?
66
+ !!@expires_at
67
+ end
68
+
69
+ # Whether or not the token is expired
70
+ #
71
+ # @return [Boolean]
72
+ def expired?
73
+ expires? && (expires_at < Time.now.to_i)
74
+ end
75
+
76
+ # Refreshes the current Access Token
77
+ #
78
+ # @return [AccessToken] a new AccessToken
79
+ # @note options should be carried over to the new AccessToken
80
+ def refresh!(params={})
81
+ raise "A refresh_token is not available" unless refresh_token
82
+ params.merge!(:client_id => @client.id,
83
+ :client_secret => @client.secret,
84
+ :grant_type => 'refresh_token',
85
+ :refresh_token => refresh_token)
86
+ new_token = @client.get_token(params)
87
+ new_token.options = options
88
+ new_token
89
+ end
90
+
91
+ # Make a request with the Access Token
92
+ #
93
+ # @param [Symbol] verb the HTTP request method
94
+ # @param [String] path the HTTP URL path of the request
95
+ # @param [Hash] opts the options to make the request with
96
+ # @see Client#request
97
+ def request(verb, path, opts={}, &block)
98
+ set_token(opts)
99
+ @client.request(verb, path, opts, &block)
100
+ end
101
+
102
+ # Make a GET request with the Access Token
103
+ #
104
+ # @see AccessToken#request
105
+ def get(path, opts={}, &block)
106
+ request(:get, path, opts, &block)
107
+ end
108
+
109
+ # Make a POST request with the Access Token
110
+ #
111
+ # @see AccessToken#request
112
+ def post(path, opts={}, &block)
113
+ request(:post, path, opts, &block)
114
+ end
115
+
116
+ # Make a PUT request with the Access Token
117
+ #
118
+ # @see AccessToken#request
119
+ def put(path, opts={}, &block)
120
+ request(:put, path, opts, &block)
121
+ end
122
+
123
+ # Make a DELETE request with the Access Token
124
+ #
125
+ # @see AccessToken#request
126
+ def delete(path, opts={}, &block)
127
+ request(:delete, path, opts, &block)
128
+ end
129
+
130
+ # Get the headers hash (includes Authorization token)
131
+ def headers
132
+ { 'Authorization' => options[:header_format] % token }
133
+ end
134
+
135
+ private
136
+ def set_token(opts)
137
+ case options[:mode]
138
+ when :header
139
+ opts[:headers] ||= {}
140
+ opts[:headers].merge!(headers)
141
+ when :query
142
+ opts[:params] ||= {}
143
+ opts[:params][options[:param_name]] = token
144
+ when :body
145
+ opts[:body] ||= {}
146
+ if opts[:body].is_a?(Hash)
147
+ opts[:body][options[:param_name]] = token
148
+ else
149
+ opts[:body] << "&#{options[:param_name]}=#{token}"
150
+ end
151
+ # @todo support for multi-part (file uploads)
152
+ else
153
+ raise "invalid :mode option of #{options[:mode]}"
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,168 @@
1
+ require 'faraday'
2
+
3
+ module OAuth2
4
+ # The OAuth2::Client class
5
+ class Client
6
+ attr_reader :id, :secret
7
+ attr_accessor :site, :connection, :options
8
+
9
+ # Instantiate a new OAuth 2.0 client using the
10
+ # Client ID and Client Secret registered to your
11
+ # application.
12
+ #
13
+ # @param [String] client_id the client_id value
14
+ # @param [String] client_secret the client_secret value
15
+ # @param [Hash] opts the options to create the client with
16
+ # @option opts [String] :site the OAuth2 provider site host
17
+ # @option opts [String] :authorize_url ('/oauth/authorize') absolute or relative URL path to the Authorization endpoint
18
+ # @option opts [String] :token_url ('/oauth/token') absolute or relative URL path to the Token endpoint
19
+ # @option opts [Symbol] :token_method (:post) HTTP method to use to request token (:get or :post)
20
+ # @option opts [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with
21
+ # @option opts [FixNum] :max_redirects (5) maximum number of redirects to follow
22
+ # @option opts [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error
23
+ # on responses with 400+ status codes
24
+ # @yield [builder] The Faraday connection builder
25
+ def initialize(client_id, client_secret, opts={}, &block)
26
+ @id = client_id
27
+ @secret = client_secret
28
+ @site = opts.delete(:site)
29
+ ssl = opts.delete(:ssl)
30
+ @options = {:authorize_url => '/oauth/authorize',
31
+ :token_url => '/oauth/token',
32
+ :token_method => :post,
33
+ :connection_opts => {},
34
+ :connection_build => block,
35
+ :max_redirects => 5,
36
+ :raise_errors => true}.merge(opts)
37
+ @options[:connection_opts][:ssl] = ssl if ssl
38
+ end
39
+
40
+ # Set the site host
41
+ #
42
+ # @param [String] the OAuth2 provider site host
43
+ def site=(value)
44
+ @connection = nil
45
+ @site = value
46
+ end
47
+
48
+ # The Faraday connection object
49
+ def connection
50
+ @connection ||= begin
51
+ conn = Faraday.new(site, options[:connection_opts])
52
+ conn.build do |b|
53
+ options[:connection_build].call(b)
54
+ end if options[:connection_build]
55
+ conn
56
+ end
57
+ end
58
+
59
+ # The authorize endpoint URL of the OAuth2 provider
60
+ #
61
+ # @param [Hash] params additional query parameters
62
+ def authorize_url(params=nil)
63
+ connection.build_url(options[:authorize_url], params).to_s
64
+ end
65
+
66
+ # The token endpoint URL of the OAuth2 provider
67
+ #
68
+ # @param [Hash] params additional query parameters
69
+ def token_url(params=nil)
70
+ connection.build_url(options[:token_url], params).to_s
71
+ end
72
+
73
+ # Makes a request relative to the specified site root.
74
+ #
75
+ # @param [Symbol] verb one of :get, :post, :put, :delete
76
+ # @param [String] url URL path of request
77
+ # @param [Hash] opts the options to make the request with
78
+ # @option opts [Hash] :params additional query parameters for the URL of the request
79
+ # @option opts [Hash, String] :body the body of the request
80
+ # @option opts [Hash] :headers http request headers
81
+ # @option opts [Boolean] :raise_errors whether or not to raise an OAuth2::Error on 400+ status
82
+ # code response for this request. Will default to client option
83
+ # @option opts [Symbol] :parse @see Response::initialize
84
+ # @yield [req] The Faraday request
85
+ def request(verb, url, opts={})
86
+ url = self.connection.build_url(url, opts[:params]).to_s
87
+
88
+ response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
89
+ yield(req) if block_given?
90
+ end
91
+ response = Response.new(response, :parse => opts[:parse])
92
+
93
+ case response.status
94
+ when 301, 302, 303, 307
95
+ opts[:redirect_count] ||= 0
96
+ opts[:redirect_count] += 1
97
+ return response if opts[:redirect_count] > options[:max_redirects]
98
+ if response.status == 303
99
+ verb = :get
100
+ opts.delete(:body)
101
+ end
102
+ request(verb, response.headers['location'], opts)
103
+ when 200..299, 300..399
104
+ # on non-redirecting 3xx statuses, just return the response
105
+ response
106
+ when 400..599
107
+ e = Error.new(response)
108
+ raise e if opts[:raise_errors] || options[:raise_errors]
109
+ response.error = e
110
+ response
111
+ else
112
+ raise Error.new(response), "Unhandled status code value of #{response.status}"
113
+ end
114
+ end
115
+
116
+ # Initializes an AccessToken by making a request to the token endpoint
117
+ #
118
+ # @param [Hash] params a Hash of params for the token endpoint
119
+ # @param [Hash] access token options, to pass to the AccessToken object
120
+ # @return [AccessToken] the initalized AccessToken
121
+ def get_token(params, access_token_opts={})
122
+ opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)}
123
+ if options[:token_method] == :post
124
+ headers = params.delete(:headers)
125
+ opts[:body] = params
126
+ opts[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'}
127
+ opts[:headers].merge!(headers) if headers
128
+ else
129
+ opts[:params] = params
130
+ end
131
+ 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))
134
+ end
135
+
136
+ # The Authorization Code strategy
137
+ #
138
+ # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1
139
+ def auth_code
140
+ @auth_code ||= OAuth2::Strategy::AuthCode.new(self)
141
+ end
142
+
143
+ # The Implicit strategy
144
+ #
145
+ # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-26#section-4.2
146
+ def implicit
147
+ @implicit ||= OAuth2::Strategy::Implicit.new(self)
148
+ end
149
+
150
+ # The Resource Owner Password Credentials strategy
151
+ #
152
+ # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.3
153
+ def password
154
+ @password ||= OAuth2::Strategy::Password.new(self)
155
+ end
156
+
157
+ # The Client Credentials strategy
158
+ #
159
+ # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
160
+ def client_credentials
161
+ @client_credentials ||= OAuth2::Strategy::ClientCredentials.new(self)
162
+ end
163
+
164
+ def assertion
165
+ @assertion ||= OAuth2::Strategy::Assertion.new(self)
166
+ end
167
+ end
168
+ end