dreamwords-oauth2 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
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