omniauth-oauthio 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.md +118 -0
- data/Rakefile +8 -0
- data/example/Gemfile +4 -0
- data/example/Gemfile.lock +44 -0
- data/example/config.ru +110 -0
- data/lib/oauthio/access_token.rb +47 -0
- data/lib/oauthio/client.rb +158 -0
- data/lib/oauthio/providers/oauthio.rb +84 -0
- data/lib/oauthio/strategy/auth_code.rb +36 -0
- data/lib/omniauth/oauthio/version.rb +5 -0
- data/lib/omniauth/oauthio.rb +9 -0
- data/lib/omniauth/strategies/oauthio.rb +158 -0
- data/lib/omniauth-oauthio.rb +1 -0
- data/omniauth-oauthio.gemspec +24 -0
- data/test/helper.rb +56 -0
- data/test/support/shared_examples.rb +85 -0
- data/test/test.rb +541 -0
- metadata +123 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1dca97fafd16386f51832f166b84c5cf621d0bc3
|
4
|
+
data.tar.gz: 6a4e6e04a937c70cfcd1d80ede2ee635d6bc222d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 149abd7e8dbdb2a5cee853f596dc982b4ad26ced924d7b2a1852d8022bdee921aae34e6d0f237691b539d42b6b761cebfc64b712bd1abc22c909a77ad42fc9ea
|
7
|
+
data.tar.gz: 6db1a1e393399688ec32e1036394658d7e2554ae81805cbad6de147fa1187dd590c63676f7a35a15e9f8f09de2ad2aefa36467678022d74633499f8cb036d67a
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Jonathan Rowlands
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
omniauth-oauthio
|
2
|
+
=================
|
3
|
+
|
4
|
+
OAuth.io Strategy for OmniAuth
|
5
|
+
|
6
|
+
# TODO
|
7
|
+
|
8
|
+
Please note this strategy is still pretty experimental and is not complete
|
9
|
+
|
10
|
+
1. I am using this mainly with a pure javascript/angularjs single page application that connects to a rails api, but
|
11
|
+
there is no reason why this potentially work with a normal rails application that takes does not require javascript.
|
12
|
+
I believe there is some missing functionality there and requires further testing.
|
13
|
+
|
14
|
+
## Installing
|
15
|
+
|
16
|
+
Add to your `Gemfile`:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'omniauth-oauthio', path: 'https://github.com/jgrowl/omniauth-oauthio.git'
|
20
|
+
```
|
21
|
+
|
22
|
+
Then `bundle install`.
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
`OmniAuth::Strategies::Oauthio` is simply a Rack middleware. Read the OmniAuth docs for detailed instructions: https://github.com/intridea/omniauth.
|
27
|
+
|
28
|
+
Here's a quick example, adding the middleware to a Rails app in `config/initializers/omniauth.rb`:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
Rails.application.config.middleware.use OmniAuth::Builder do
|
32
|
+
configure do |config|
|
33
|
+
config.path_prefix = '/users/auth'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
The following steps on the front-end need to occur:
|
39
|
+
|
40
|
+
1. Initialize the OAuth public key.
|
41
|
+
|
42
|
+
2. Perform get request to initiate the request_phase, using the .json option for SPA (This is to get a state string from the server).
|
43
|
+
|
44
|
+
3. Use OAuth.io's javascript api to initiate a popup (Passing along the state from step 2).
|
45
|
+
|
46
|
+
4. Perform get request to initiate callback_phase (Passing along the state and code received in step 3).
|
47
|
+
|
48
|
+
For example: (NOTE: I need to update this. I am currently using dart in my test app)
|
49
|
+
|
50
|
+
```coffeescript
|
51
|
+
OAuth.initialize('YOUR_PUBLIC_KEY')
|
52
|
+
|
53
|
+
$.get "http://localhost:3000/users/auth/oauthio.json", (data) ->
|
54
|
+
@options = data
|
55
|
+
|
56
|
+
OAuth.popup provider, @options, (err, res) ->
|
57
|
+
if (err)
|
58
|
+
console.log err
|
59
|
+
else
|
60
|
+
$.get "http://localhost:3000/users/auth/oauthio/twitter/callback.json?state=@options.state&code=@options.code", (data) ->
|
61
|
+
console.log(data)
|
62
|
+
# Perform additional login steps
|
63
|
+
```
|
64
|
+
|
65
|
+
## Configuring
|
66
|
+
|
67
|
+
### OAuth.io
|
68
|
+
|
69
|
+
Be sure to enable the Server-side (code) option on any providers you want to use with this strategy.
|
70
|
+
|
71
|
+
### Custom Callback URL/Path
|
72
|
+
|
73
|
+
You can set a custom `callback_url` or `callback_path` option to override the default value. See [OmniAuth::Strategy#callback_url](https://github.com/intridea/omniauth/blob/master/lib/omniauth/strategy.rb#L411) for more details on the default.
|
74
|
+
|
75
|
+
### Devise
|
76
|
+
To use with devise, in `config/initializers/devise.rb`
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
config.omniauth :oauthio, ENV['OAUTHIO_PUBLIC_KEY'], ENV['OAUTHIO_SECRET_KEY']
|
80
|
+
```
|
81
|
+
|
82
|
+
### Omniauth
|
83
|
+
|
84
|
+
Add an oauthio callback in `app/controllers/users/omniauth_callbacks_controller.rb`
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
|
88
|
+
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
89
|
+
def oauthio
|
90
|
+
|
91
|
+
# TODO: Do your login logic here! ie. look up the user by the uid or create one if it does not already exist!
|
92
|
+
|
93
|
+
respond_to do |format|
|
94
|
+
format.json { render json: auth_hash}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def auth_hash
|
99
|
+
request.env['omniauth.auth']
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
Create the method used in your callback in your `user.rb`
|
106
|
+
|
107
|
+
# Understanding server side flow
|
108
|
+
|
109
|
+
oauth.io describes how everything works in their [security](https://oauth.io/docs/security) section.
|
110
|
+
|
111
|
+
![alt text](https://oauth.io/img/server-side-flow.png "Server side flow")
|
112
|
+
|
113
|
+
|
114
|
+
## Credit
|
115
|
+
|
116
|
+
https://oauth.io/
|
117
|
+
|
118
|
+
https://github.com/mkdynamic/omniauth-facebook
|
data/Rakefile
ADDED
data/example/Gemfile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
omniauth-facebook (1.6.0.rc1)
|
5
|
+
omniauth-oauth2 (~> 1.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
faraday (0.8.8)
|
11
|
+
multipart-post (~> 1.2.0)
|
12
|
+
hashie (2.0.5)
|
13
|
+
httpauth (0.2.0)
|
14
|
+
jwt (0.1.8)
|
15
|
+
multi_json (>= 1.5)
|
16
|
+
multi_json (1.8.2)
|
17
|
+
multipart-post (1.2.0)
|
18
|
+
oauth2 (0.8.1)
|
19
|
+
faraday (~> 0.8)
|
20
|
+
httpauth (~> 0.1)
|
21
|
+
jwt (~> 0.1.4)
|
22
|
+
multi_json (~> 1.0)
|
23
|
+
rack (~> 1.2)
|
24
|
+
omniauth (1.1.4)
|
25
|
+
hashie (>= 1.2, < 3)
|
26
|
+
rack
|
27
|
+
omniauth-oauth2 (1.1.1)
|
28
|
+
oauth2 (~> 0.8.0)
|
29
|
+
omniauth (~> 1.0)
|
30
|
+
rack (1.5.2)
|
31
|
+
rack-protection (1.5.1)
|
32
|
+
rack
|
33
|
+
sinatra (1.4.4)
|
34
|
+
rack (~> 1.4)
|
35
|
+
rack-protection (~> 1.4)
|
36
|
+
tilt (~> 1.3, >= 1.3.4)
|
37
|
+
tilt (1.4.1)
|
38
|
+
|
39
|
+
PLATFORMS
|
40
|
+
ruby
|
41
|
+
|
42
|
+
DEPENDENCIES
|
43
|
+
omniauth-facebook!
|
44
|
+
sinatra
|
data/example/config.ru
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'sinatra/base'
|
3
|
+
require 'omniauth-oauthio'
|
4
|
+
|
5
|
+
#SCOPE = 'email,read_stream'
|
6
|
+
#
|
7
|
+
#class App < Sinatra::Base
|
8
|
+
# # turn off sinatra default X-Frame-Options for FB canvas
|
9
|
+
# set :protection, :except => :frame_options
|
10
|
+
#
|
11
|
+
# # server-side flow
|
12
|
+
# get '/' do
|
13
|
+
# # NOTE: you would just hit this endpoint directly from the browser
|
14
|
+
# # in a real app. the redirect is just here to setup the root
|
15
|
+
# # path in this example sinatra app.
|
16
|
+
# redirect '/auth/facebook'
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # client-side flow
|
20
|
+
# get '/client-side' do
|
21
|
+
# content_type 'text/html'
|
22
|
+
# # NOTE: when you enable cookie below in the FB.init call
|
23
|
+
# # the GET request in the FB.login callback will send
|
24
|
+
# # a signed request in a cookie back the OmniAuth callback
|
25
|
+
# # which will parse out the authorization code and obtain
|
26
|
+
# # the access_token. This will be the exact same access_token
|
27
|
+
# # returned to the client in response.authResponse.accessToken.
|
28
|
+
# <<-END
|
29
|
+
# <html>
|
30
|
+
# <head>
|
31
|
+
# <title>Client-side Flow Example</title>
|
32
|
+
# <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js" type="text/javascript"></script>
|
33
|
+
# </head>
|
34
|
+
# <body>
|
35
|
+
# <div id="fb-root"></div>
|
36
|
+
#
|
37
|
+
# <script type="text/javascript">
|
38
|
+
# window.fbAsyncInit = function() {
|
39
|
+
# FB.init({
|
40
|
+
# appId : '#{ENV['APP_ID']}',
|
41
|
+
# status : true, // check login status
|
42
|
+
# cookie : true, // enable cookies to allow the server to access the session
|
43
|
+
# xfbml : true // parse XFBML
|
44
|
+
# });
|
45
|
+
# };
|
46
|
+
#
|
47
|
+
# (function(d) {
|
48
|
+
# var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
|
49
|
+
# js = d.createElement('script'); js.id = id; js.async = true;
|
50
|
+
# js.src = "//connect.facebook.net/en_US/all.js";
|
51
|
+
# d.getElementsByTagName('head')[0].appendChild(js);
|
52
|
+
# }(document));
|
53
|
+
#
|
54
|
+
# $(function() {
|
55
|
+
# $('a').click(function(e) {
|
56
|
+
# e.preventDefault();
|
57
|
+
#
|
58
|
+
# FB.login(function(response) {
|
59
|
+
# if (response.authResponse) {
|
60
|
+
# $('#connect').html('Connected! Hitting OmniAuth callback (GET /auth/facebook/callback)...');
|
61
|
+
#
|
62
|
+
# // since we have cookies enabled, this request will allow omniauth to parse
|
63
|
+
# // out the auth code from the signed request in the fbsr_XXX cookie
|
64
|
+
# $.getJSON('/auth/facebook/callback', function(json) {
|
65
|
+
# $('#connect').html('Connected! Callback complete.');
|
66
|
+
# $('#results').html(JSON.stringify(json));
|
67
|
+
# });
|
68
|
+
# }
|
69
|
+
# }, { scope: '#{SCOPE}' });
|
70
|
+
# });
|
71
|
+
# });
|
72
|
+
# </script>
|
73
|
+
#
|
74
|
+
# <p id="connect">
|
75
|
+
# <a href="#">Connect to FB</a>
|
76
|
+
# </p>
|
77
|
+
#
|
78
|
+
# <p id="results" />
|
79
|
+
# </body>
|
80
|
+
# </html>
|
81
|
+
# END
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# # auth via FB canvas and signed request param
|
85
|
+
# post '/canvas/' do
|
86
|
+
# # we just redirect to /auth/facebook here which will parse the
|
87
|
+
# # signed_request FB sends us, asking for auth if the user has
|
88
|
+
# # not already granted access, or simply moving straight to the
|
89
|
+
# # callback where they have already granted access.
|
90
|
+
# redirect "/auth/facebook?signed_request=#{request.params['signed_request']}"
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# get '/auth/:provider/callback' do
|
94
|
+
# content_type 'application/json'
|
95
|
+
# MultiJson.encode(request.env)
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# get '/auth/failure' do
|
99
|
+
# content_type 'application/json'
|
100
|
+
# MultiJson.encode(request.env)
|
101
|
+
# end
|
102
|
+
#end
|
103
|
+
#
|
104
|
+
#use Rack::Session::Cookie
|
105
|
+
#
|
106
|
+
#use OmniAuth::Builder do
|
107
|
+
# provider :facebook, ENV['APP_ID'], ENV['APP_SECRET'], :scope => SCOPE
|
108
|
+
#end
|
109
|
+
#
|
110
|
+
#run App.new
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Oauthio
|
2
|
+
class AccessToken < OAuth2::AccessToken
|
3
|
+
attr_reader :provider, :oauth_token, :oauth_token_secret
|
4
|
+
|
5
|
+
class << self
|
6
|
+
# Initializes an AccessToken from a Hash
|
7
|
+
#
|
8
|
+
# @param [Client] the OAuth2::Client instance
|
9
|
+
# @param [Hash] a hash of AccessToken property values
|
10
|
+
# @return [AccessToken] the initalized AccessToken
|
11
|
+
def from_hash(client, hash)
|
12
|
+
# new(client, hash.delete('access_token') || hash.delete(:access_token), hash)
|
13
|
+
new(client,
|
14
|
+
hash.delete('provider') || hash.delete(:provider),
|
15
|
+
hash.delete('access_token') || hash.delete(:access_token),
|
16
|
+
hash.delete('oauth_token') || hash.delete(:oauth_token),
|
17
|
+
hash.delete('oauth_token_secret') || hash.delete(:oauth_token_secret),
|
18
|
+
hash)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(client, provider, token, oauth_token, oauth_token_secret, opts = {})
|
23
|
+
super client, token, opts
|
24
|
+
@provider = provider
|
25
|
+
@oauth_token = oauth_token.to_s
|
26
|
+
@oauth_token_secret = oauth_token_secret.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
def me()
|
30
|
+
k = @client.id
|
31
|
+
# oauthv = 1 # TODO: Update this
|
32
|
+
|
33
|
+
if !@token.empty?
|
34
|
+
# oauthv=#{oauthv}
|
35
|
+
oauthio_header = "k=#{k}&access_token=#{@token}"
|
36
|
+
elsif !@oauth_token.empty? && !@oauth_token_secret.empty?
|
37
|
+
# oauthv=#{oauthv}
|
38
|
+
oauthio_header = "k=#{k}&oauth_token=#{@oauth_token}&oauth_token_secret=#{@oauth_token_secret}"
|
39
|
+
else
|
40
|
+
# TODO: Throw error if no tokens found
|
41
|
+
end
|
42
|
+
opts = {headers: {oauthio: oauthio_header}}
|
43
|
+
me_url = client.me_url(provider)
|
44
|
+
request(:get, me_url, opts)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module Oauthio
|
2
|
+
class Client < ::OAuth2::Client
|
3
|
+
|
4
|
+
# Instantiate a new OAuth 2.0 client using the
|
5
|
+
# Client ID and Client Secret registered to your
|
6
|
+
# application.
|
7
|
+
#
|
8
|
+
# @param [String] client_id the client_id value
|
9
|
+
# @param [String] client_secret the client_secret value
|
10
|
+
# @param [Hash] opts the options to create the client with
|
11
|
+
# @option opts [String] :site the OAuth2 provider site host
|
12
|
+
# @option opts [String] :authorize_url ('/oauth/authorize') absolute or relative URL path to the Authorization endpoint
|
13
|
+
# @option opts [String] :token_url ('/oauth/token') absolute or relative URL path to the Token endpoint
|
14
|
+
# @option opts [Symbol] :token_method (:post) HTTP method to use to request token (:get or :post)
|
15
|
+
# @option opts [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with
|
16
|
+
# @option opts [FixNum] :max_redirects (5) maximum number of redirects to follow
|
17
|
+
# @option opts [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error
|
18
|
+
# on responses with 400+ status codes
|
19
|
+
# @yield [builder] The Faraday connection builder
|
20
|
+
def initialize(client_id, client_secret, opts = {}, &block)
|
21
|
+
_opts = opts.dup
|
22
|
+
@id = client_id
|
23
|
+
@secret = client_secret
|
24
|
+
@site = _opts.delete(:site)
|
25
|
+
@state = _opts.delete(:state)
|
26
|
+
ssl = _opts.delete(:ssl)
|
27
|
+
@options = {:authorize_url => '/auth',
|
28
|
+
:token_url => '/auth/access_token',
|
29
|
+
:me_url => '/auth/:provider/me',
|
30
|
+
:token_method => :post,
|
31
|
+
:connection_opts => {},
|
32
|
+
:connection_build => block,
|
33
|
+
:max_redirects => 5,
|
34
|
+
:raise_errors => true}.merge(_opts)
|
35
|
+
@options[:connection_opts][:ssl] = ssl if ssl
|
36
|
+
end
|
37
|
+
|
38
|
+
def me_url(provider, params = nil)
|
39
|
+
connection.build_url(options[:me_url], params).to_s.sub(/:provider/, provider)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Makes a request relative to the specified site root.
|
43
|
+
#
|
44
|
+
# @param [Symbol] verb one of :get, :post, :put, :delete
|
45
|
+
# @param [String] url URL path of request
|
46
|
+
# @param [Hash] opts the options to make the request with
|
47
|
+
# @option opts [Hash] :params additional query parameters for the URL of the request
|
48
|
+
# @option opts [Hash, String] :body the body of the request
|
49
|
+
# @option opts [Hash] :headers http request headers
|
50
|
+
# @option opts [Boolean] :raise_errors whether or not to raise an OAuth2::Error on 400+ status
|
51
|
+
# code response for this request. Will default to client option
|
52
|
+
# @option opts [Symbol] :parse @see Response::initialize
|
53
|
+
# @yield [req] The Faraday request
|
54
|
+
def request(verb, url, opts = {}) # rubocop:disable CyclomaticComplexity, MethodLength
|
55
|
+
url = connection.build_url(url, opts[:params]).to_s
|
56
|
+
|
57
|
+
response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
|
58
|
+
yield(req) if block_given?
|
59
|
+
end
|
60
|
+
|
61
|
+
# Only really care about the status and the actual return body.
|
62
|
+
# Oauth2 strategy wraps the response in a Response object that handles parsing and whatnot. That is great when
|
63
|
+
# support for multiple options is needed, however we only have to conform to a single interface. We will take
|
64
|
+
# the easy route of always expecting a json response.
|
65
|
+
status = response.status
|
66
|
+
headers = response.headers
|
67
|
+
response = JSON.parse(response.body)
|
68
|
+
response['status'] = status
|
69
|
+
response['headers'] = headers
|
70
|
+
response = Hashie::Mash.new response
|
71
|
+
|
72
|
+
case response.status
|
73
|
+
when 301, 302, 303, 307
|
74
|
+
opts[:redirect_count] ||= 0
|
75
|
+
opts[:redirect_count] += 1
|
76
|
+
return response if opts[:redirect_count] > options[:max_redirects]
|
77
|
+
if response.status == 303
|
78
|
+
verb = :get
|
79
|
+
opts.delete(:body)
|
80
|
+
end
|
81
|
+
request(verb, response.headers['location'], opts)
|
82
|
+
when 200..299, 300..399
|
83
|
+
# on non-redirecting 3xx statuses, just return the response
|
84
|
+
response
|
85
|
+
when 400..599
|
86
|
+
error = OAuth2::Error.new(response)
|
87
|
+
fail(error) if opts.fetch(:raise_errors, options[:raise_errors])
|
88
|
+
response.error = error
|
89
|
+
response
|
90
|
+
else
|
91
|
+
error = OAuth2::Error.new(response)
|
92
|
+
fail(error, "Unhandled status code value of #{response.status}")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Initializes an AccessToken by making a request to the token endpoint
|
97
|
+
#
|
98
|
+
# @param [Hash] params a Hash of params for the token endpoint
|
99
|
+
# @param [Hash] access token options, to pass to the AccessToken object
|
100
|
+
# @param [Class] class of access token for easier subclassing OAuth2::AccessToken
|
101
|
+
# @return [AccessToken] the initalized AccessToken
|
102
|
+
def get_token(params, access_token_opts = {}, access_token_class = AccessToken)
|
103
|
+
opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)}
|
104
|
+
if options[:token_method] == :post
|
105
|
+
headers = params.delete(:headers)
|
106
|
+
opts[:body] = params
|
107
|
+
opts[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'}
|
108
|
+
opts[:headers].merge!(headers) if headers
|
109
|
+
else
|
110
|
+
opts[:params] = params
|
111
|
+
end
|
112
|
+
response = request(options[:token_method], token_url, opts)
|
113
|
+
|
114
|
+
# Verify state in the response matches the one in the session
|
115
|
+
if response.state != @state
|
116
|
+
raise ::OmniAuth::Strategies::OAuth2::CallbackError.new(nil, :csrf_detected);
|
117
|
+
end
|
118
|
+
|
119
|
+
# error = Error.new(response)
|
120
|
+
# fail(error) if options[:raise_errors] && !(response.parsed.is_a?(Hash) && response.parsed['access_token'])
|
121
|
+
|
122
|
+
provider_client = ::Oauthio::Client.new(@id, @secret, { :site => @site })
|
123
|
+
access_token_class.from_hash(provider_client, response.merge(access_token_opts))
|
124
|
+
end
|
125
|
+
|
126
|
+
# The Authorization Code strategy
|
127
|
+
#
|
128
|
+
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1
|
129
|
+
def auth_code
|
130
|
+
@auth_code ||= Oauthio::Strategy::AuthCode.new(self)
|
131
|
+
end
|
132
|
+
|
133
|
+
# The Implicit strategy
|
134
|
+
#
|
135
|
+
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-26#section-4.2
|
136
|
+
def implicit
|
137
|
+
@implicit ||= OAuth2::Strategy::Implicit.new(self)
|
138
|
+
end
|
139
|
+
|
140
|
+
# The Resource Owner Password Credentials strategy
|
141
|
+
#
|
142
|
+
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.3
|
143
|
+
def password
|
144
|
+
@password ||= OAuth2::Strategy::Password.new(self)
|
145
|
+
end
|
146
|
+
|
147
|
+
# The Client Credentials strategy
|
148
|
+
#
|
149
|
+
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
|
150
|
+
def client_credentials
|
151
|
+
@client_credentials ||= OAuth2::Strategy::ClientCredentials.new(self)
|
152
|
+
end
|
153
|
+
|
154
|
+
def assertion
|
155
|
+
@assertion ||= OAuth2::Strategy::Assertion.new(self)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Oauthio
|
2
|
+
module Providers
|
3
|
+
class Oauthio
|
4
|
+
def initialize(access_token, secret, options)
|
5
|
+
@access_token = access_token
|
6
|
+
@secret = secret
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def uid
|
11
|
+
# This might not be uniform across all providers. Need to talk to oauthd guys to see if we can get the id
|
12
|
+
# in the list of things parsed out of the raw data.
|
13
|
+
raw_info['id']
|
14
|
+
end
|
15
|
+
|
16
|
+
def skip_info?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def info
|
21
|
+
prune!({
|
22
|
+
'name' => _raw_info['name'],
|
23
|
+
'alias' => _raw_info['alias'],
|
24
|
+
'bio' => _raw_info['bio'],
|
25
|
+
'avatar' => _raw_info['avatar'],
|
26
|
+
'firstname' => _raw_info['firstname'],
|
27
|
+
'lastname' => _raw_info['lastname'],
|
28
|
+
'gender' => _raw_info['gender'],
|
29
|
+
'location' => _raw_info['location'],
|
30
|
+
'local' => _raw_info['local'],
|
31
|
+
'company' => _raw_info['company'],
|
32
|
+
'occupation' => _raw_info['occupation'],
|
33
|
+
'language' => _raw_info['language'],
|
34
|
+
'birthdate' => _raw_info['birthdate'],
|
35
|
+
})
|
36
|
+
end
|
37
|
+
|
38
|
+
def extra
|
39
|
+
hash = {}
|
40
|
+
hash['raw_info'] = raw_info unless skip_info?
|
41
|
+
prune! hash
|
42
|
+
end
|
43
|
+
|
44
|
+
def _raw_info
|
45
|
+
@_raw_info ||= @access_token.me()['data'] || {}
|
46
|
+
@_raw_info
|
47
|
+
end
|
48
|
+
|
49
|
+
def raw_info
|
50
|
+
@raw_info ||= _raw_info['raw'] || {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def info_options
|
54
|
+
# params = {:appsecret_proof => appsecret_proof}
|
55
|
+
# params.merge!({:fields => @options[:info_fields]}) if @options[:info_fields]
|
56
|
+
# params.merge!({:locale => @options[:locale]}) if @options[:locale]
|
57
|
+
#
|
58
|
+
# {:params => params}
|
59
|
+
end
|
60
|
+
|
61
|
+
def appsecret_proof
|
62
|
+
# @appsecret_proof ||= OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @secret, @access_token.token)
|
63
|
+
end
|
64
|
+
|
65
|
+
def credentials
|
66
|
+
hash = {}
|
67
|
+
hash.merge!('token' => @access_token.token) if !@access_token.token.empty?
|
68
|
+
hash.merge!('oauth_token' => @access_token.oauth_token,
|
69
|
+
'oauth_token_secret' => @access_token.oauth_token_secret) if !@access_token.oauth_token.empty? && !@access_token.oauth_token_secret.empty?
|
70
|
+
hash.merge!('refresh_token' => @access_token.refresh_token) if @access_token.expires? && @access_token.refresh_token
|
71
|
+
hash.merge!('expires_at' => @access_token.expires_at) if @access_token.expires?
|
72
|
+
hash.merge!('expires' => @access_token.expires?)
|
73
|
+
hash
|
74
|
+
end
|
75
|
+
|
76
|
+
def prune!(hash)
|
77
|
+
hash.delete_if do |_, v|
|
78
|
+
prune!(v) if v.is_a?(Hash)
|
79
|
+
v.nil? || (v.respond_to?(:empty?) && v.empty?)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Oauthio
|
2
|
+
module Strategy
|
3
|
+
class AuthCode < OAuth2::Strategy::AuthCode
|
4
|
+
def initialize(client)
|
5
|
+
@client = client
|
6
|
+
end
|
7
|
+
|
8
|
+
# The required query parameters for the authorize URL
|
9
|
+
#
|
10
|
+
# @param [Hash] params additional query parameters
|
11
|
+
def authorize_params(params={})
|
12
|
+
params.merge('k' => @client.id)
|
13
|
+
end
|
14
|
+
|
15
|
+
#TODO: Put this in base.rb
|
16
|
+
# The OAuth client_id and client_secret
|
17
|
+
#
|
18
|
+
# @return [Hash]
|
19
|
+
def client_params
|
20
|
+
{'key' => @client.id, 'secret' => @client.secret}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Retrieve an access token given the specified validation code.
|
24
|
+
#
|
25
|
+
# @param [String] code The Authorization Code value
|
26
|
+
# @param [Hash] params additional params
|
27
|
+
# @param [Hash] opts options
|
28
|
+
# @note that you must also provide a :redirect_uri with most OAuth 2.0 providers
|
29
|
+
def get_token(code, params = {}, opts = {})
|
30
|
+
params = {'code' => code}.merge(client_params).merge(params)
|
31
|
+
@client.get_token(params, opts)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|