omniauth-oauthio 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.rspec +3 -0
- data/README.md +90 -70
- data/Rakefile +3 -5
- data/example/Gemfile +1 -1
- data/example/app.rb +14 -9
- data/example/config.ru +3 -2
- data/lib/oauthio/access_token.rb +13 -11
- data/lib/oauthio/client.rb +43 -37
- data/lib/oauthio/providers/oauthio.rb +29 -21
- data/lib/oauthio/strategy/auth_code.rb +1 -2
- data/lib/omniauth/oauthio.rb +0 -2
- data/lib/omniauth/oauthio/version.rb +1 -1
- data/lib/omniauth/strategies/oauthio.rb +70 -44
- data/omniauth-oauthio.gemspec +2 -1
- data/spec/client_spec.rb +102 -0
- data/spec/oauthio_spec.rb +144 -0
- data/spec/spec_helper.rb +30 -0
- metadata +26 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 70606edfa2af2716b721133a36072b165f60c210
|
4
|
+
data.tar.gz: 4be6085639515da6d8d873369b6af0b6f838a55a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 321dd9cb4655504e3c663bef40d2858aba636f6b61e9b67223e836795191fe2c8ba28f9f75a0734a65071e65532efc53acea399eaea1b479e112287c5f61a911
|
7
|
+
data.tar.gz: 6b44d837670e892a6e89e854e1f6433f98145a3ead21250af3c61972aedbb581cad8cad0f97f97d41db77ef16bc9139f2d3e68559d95ffa4a5e4dece865fc8a4
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/README.md
CHANGED
@@ -1,129 +1,149 @@
|
|
1
1
|
omniauth-oauthio
|
2
2
|
=================
|
3
3
|
|
4
|
-
OAuth.io Strategy for OmniAuth
|
4
|
+
[OAuth.io](https://oauth.io/) Strategy for OmniAuth
|
5
5
|
|
6
6
|
## Installing
|
7
7
|
|
8
8
|
Add to your `Gemfile`:
|
9
9
|
|
10
10
|
```ruby
|
11
|
-
gem 'omniauth-oauthio',
|
11
|
+
gem 'omniauth-oauthio', '~> 0.2.1'
|
12
12
|
```
|
13
13
|
|
14
14
|
Then `bundle install`.
|
15
15
|
|
16
16
|
## Usage
|
17
17
|
|
18
|
-
`OmniAuth::Strategies::Oauthio` is simply a Rack middleware. Read the OmniAuth docs for detailed instructions: https://github.com/intridea/omniauth.
|
19
|
-
|
20
|
-
Here's a quick example, adding the middleware to a Rails app in `config/initializers/omniauth.rb`:
|
21
|
-
|
22
|
-
```ruby
|
23
|
-
Rails.application.config.middleware.use OmniAuth::Builder do
|
24
|
-
configure do |config|
|
25
|
-
config.path_prefix = '/users/auth'
|
26
|
-
end
|
27
|
-
end
|
28
|
-
```
|
29
|
-
|
30
18
|
The following steps on the front-end need to occur:
|
31
19
|
|
32
20
|
1. Initialize the OAuth public key.
|
33
21
|
|
34
|
-
2. Perform
|
35
|
-
|
36
|
-
3.
|
37
|
-
|
38
|
-
4. Perform
|
39
|
-
|
40
|
-
For example:
|
41
|
-
|
42
|
-
```
|
43
|
-
OAuth.initialize('YOUR_PUBLIC_KEY')
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
22
|
+
2. Perform a request to initiate the request_phase `/auth/oauthio/:provider`, using the .json option for a single-page application (this is to get a state string from the server).
|
23
|
+
|
24
|
+
3. Optionally, use OAuth.io's JavaScript API to initiate a popup or a redirect, passing along the state from step 2.
|
25
|
+
|
26
|
+
4. Perform a request to initiate callback_phase `/auth/oauthio/:provider/callback`, passing along the state and code received in step 3.
|
27
|
+
|
28
|
+
For example:
|
29
|
+
|
30
|
+
```javascript
|
31
|
+
OAuth.initialize('YOUR_PUBLIC_KEY');
|
32
|
+
|
33
|
+
var selectedProvider = $('#provider').val();
|
34
|
+
var type = $('#type').val();
|
35
|
+
|
36
|
+
$.get("/auth/oauthio/" + selectedProvider + ".json").done(function(data){
|
37
|
+
var state = data.state
|
38
|
+
if (type == 'popup') {
|
39
|
+
OAuth.popup(selectedProvider, {'state': state})
|
40
|
+
.done(function(result) {
|
41
|
+
//use result.access_token in your API request
|
42
|
+
//or use result.get|post|put|del|patch|me methods (see below)
|
43
|
+
result.me().done(function(me){
|
44
|
+
$('#me').html(JSON.stringify(me));
|
45
|
+
$.post("/auth/oauthio/" + selectedProvider + "/callback.json", {'state': state, 'code': result.code})
|
46
|
+
.done(function(r){
|
47
|
+
$('#results').html(JSON.stringify(r));
|
48
|
+
});
|
49
|
+
});
|
50
|
+
})
|
51
|
+
.fail(function (err) {
|
52
|
+
//handle error with err
|
53
|
+
console.log(err);
|
54
|
+
$('#results').html(err.message)
|
55
|
+
});
|
56
|
+
} else if (type == 'redirect') {
|
57
|
+
OAuth.redirect(selectedProvider, {'state': state}, '/client-side?provider=' + selectedProvider + '&state=' + state);
|
58
|
+
}
|
55
59
|
```
|
56
60
|
|
57
61
|
## Configuring
|
58
62
|
|
59
|
-
###
|
63
|
+
### Rails
|
60
64
|
|
61
|
-
|
65
|
+
Set `path_prefix` in `config/initializers/omniauth.rb`:
|
62
66
|
|
63
|
-
|
67
|
+
```ruby
|
68
|
+
Rails.application.config.middleware.use OmniAuth::Builder do
|
69
|
+
configure do |config|
|
70
|
+
config.path_prefix = '/users/auth'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
```
|
64
74
|
|
65
|
-
|
75
|
+
#### Devise
|
66
76
|
|
67
|
-
|
68
|
-
To use with devise, in `config/initializers/devise.rb`
|
77
|
+
To use with [Devise](https://github.com/plataformatec/devise), in `config/initializers/devise.rb`
|
69
78
|
|
70
79
|
```ruby
|
71
80
|
config.omniauth :oauthio, ENV['OAUTHIO_PUBLIC_KEY'], ENV['OAUTHIO_SECRET_KEY']
|
72
81
|
```
|
73
82
|
|
74
|
-
Add your
|
83
|
+
Add your Devise routes in `config/routes.rb`:
|
75
84
|
|
76
85
|
```ruby
|
77
86
|
devise_for :users, :skip => [:omniauth_callbacks]
|
78
87
|
devise_scope :user do
|
79
|
-
match
|
80
|
-
constraints: {
|
81
|
-
to:
|
82
|
-
as: :omniauth_authorize,
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
as: :omniauth_callback,
|
89
|
-
via: [:get, :post]
|
88
|
+
match '/users/auth/:action(/:sub_action)',
|
89
|
+
constraints: {:action => /oauthio/},
|
90
|
+
to: 'users/omniauth_callbacks#passthru',
|
91
|
+
as: :omniauth_authorize, via: [:get, :post]
|
92
|
+
|
93
|
+
match '/users/auth/:action(/:sub_action)/callback',
|
94
|
+
constraints: {:action => /oauthio/},
|
95
|
+
to: 'users/omniauth_callbacks',
|
96
|
+
as: :omniauth_callback, via: [:get, :post]
|
90
97
|
end
|
91
98
|
```
|
92
99
|
|
100
|
+
`sub_action` options are available if you would like to limit the actual providers allowed on the Rails side:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
constraints: {:action => /oauthio/, :sub_action => /twitter|google/}
|
104
|
+
```
|
105
|
+
|
106
|
+
### OAuth.io
|
107
|
+
|
108
|
+
Be sure to enable the server-side (code) option on any providers you want to use with this strategy.
|
109
|
+
|
93
110
|
### Omniauth
|
94
111
|
|
95
|
-
Add an oauthio callback in `app/controllers/users/omniauth_callbacks_controller.rb
|
112
|
+
Add an `oauthio` callback in `app/controllers/users/omniauth_callbacks_controller.rb`:
|
96
113
|
|
97
114
|
```ruby
|
98
|
-
|
99
115
|
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
100
116
|
def oauthio
|
101
|
-
|
102
|
-
#
|
117
|
+
# TODO: Do your login logic here!
|
118
|
+
# e.g., look up the user by the uid or create one if it does not already
|
119
|
+
# exist!
|
103
120
|
|
104
121
|
respond_to do |format|
|
105
|
-
format.json { render json:
|
122
|
+
format.json { render json: request.env['omniauth.auth'] }
|
106
123
|
end
|
107
124
|
end
|
108
|
-
|
109
|
-
def auth_hash
|
110
|
-
request.env['omniauth.auth']
|
111
|
-
end
|
112
|
-
|
113
125
|
end
|
114
126
|
```
|
115
127
|
|
116
|
-
|
117
|
-
|
118
|
-
# Understanding server side flow
|
128
|
+
## Understanding server-side flow
|
119
129
|
|
120
|
-
|
130
|
+
OAuth.io describes how everything works in [their security section](https://oauth.io/docs/security):
|
121
131
|
|
122
132
|
![alt text](https://oauth.io/img/server-side-flow.png "Server side flow")
|
123
133
|
|
134
|
+
## Running Sample Application
|
124
135
|
|
125
|
-
|
136
|
+
In `example/` there is a simple Sinatra app that uses this gem. You can test Facebook, Twitter, and Google authentication with it. For these providers to work, you'll have to set them up on the provider website, e.g., [Facebook Developers](https://developers.facebook.com), as well as in OAuth.io.
|
137
|
+
|
138
|
+
To start the sample app:
|
139
|
+
|
140
|
+
cd example
|
141
|
+
bundle
|
142
|
+
OAUTHIO_PUBLIC_KEY=yourkey OAUTHIO_PRIVATE_KEY=yourprivatekey rackup
|
126
143
|
|
127
|
-
|
144
|
+
Then visit [http://localhost:9292](http://localhost:9292) in your browser.
|
145
|
+
|
146
|
+
## Credit
|
128
147
|
|
129
|
-
https://
|
148
|
+
- [OAuth.io](https://oauth.io/)
|
149
|
+
- [omniauth-facebook](https://github.com/mkdynamic/omniauth-facebook)
|
data/Rakefile
CHANGED
data/example/Gemfile
CHANGED
data/example/app.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
require 'bundler/setup'
|
1
2
|
require 'sinatra'
|
2
|
-
require
|
3
|
+
require 'sinatra/reloader'
|
3
4
|
require 'yaml'
|
4
5
|
|
5
6
|
# configure sinatra
|
@@ -7,7 +8,7 @@ set :run, false
|
|
7
8
|
set :raise_errors, true
|
8
9
|
|
9
10
|
# setup logging to file
|
10
|
-
log = File.new(
|
11
|
+
log = File.new('app.log', 'a+')
|
11
12
|
$stdout.reopen(log)
|
12
13
|
$stderr.reopen(log)
|
13
14
|
$stderr.sync = true
|
@@ -15,15 +16,15 @@ $stdout.sync = true
|
|
15
16
|
|
16
17
|
# server-side flow
|
17
18
|
get '/server-side/:provider' do
|
18
|
-
# NOTE: You would just hit this endpoint directly from the browser in a real
|
19
|
-
#
|
19
|
+
# NOTE: You would just hit this endpoint directly from the browser in a real
|
20
|
+
# app. The redirect is just here to explicitly declare this server-side flow.
|
20
21
|
redirect "/auth/oauthio/#{params[:provider]}"
|
21
22
|
end
|
22
23
|
|
23
24
|
# client-side flow
|
24
25
|
get '/client-side' do
|
25
26
|
content_type 'text/html'
|
26
|
-
<<-END
|
27
|
+
<<-END.gsub(/^\s{4}/, '')
|
27
28
|
<html>
|
28
29
|
<head>
|
29
30
|
<title>Client-side Flow Example</title>
|
@@ -136,9 +137,9 @@ get '/client-side' do
|
|
136
137
|
END
|
137
138
|
end
|
138
139
|
|
139
|
-
def self.get_or_post(url
|
140
|
-
get(url
|
141
|
-
post(url
|
140
|
+
def self.get_or_post(url, &block)
|
141
|
+
get(url, &block)
|
142
|
+
post(url, &block)
|
142
143
|
end
|
143
144
|
|
144
145
|
get_or_post '/auth/:provider/:sub_provider/callback.?:format?' do
|
@@ -149,4 +150,8 @@ end
|
|
149
150
|
get '/auth/failure' do
|
150
151
|
content_type 'application/json'
|
151
152
|
MultiJson.encode(request.env)
|
152
|
-
end
|
153
|
+
end
|
154
|
+
|
155
|
+
get '/' do
|
156
|
+
redirect '/client-side'
|
157
|
+
end
|
data/example/config.ru
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'bundler/setup'
|
2
2
|
require 'omniauth-oauthio'
|
3
|
-
require '
|
3
|
+
require 'sinatra'
|
4
|
+
require_relative 'app.rb'
|
4
5
|
|
5
6
|
use Rack::Session::Cookie, :secret => 'abc123'
|
6
7
|
|
@@ -8,4 +9,4 @@ use OmniAuth::Builder do
|
|
8
9
|
provider :oauthio, ENV['OAUTHIO_PUBLIC_KEY'], ENV['OAUTHIO_PRIVATE_KEY']
|
9
10
|
end
|
10
11
|
|
11
|
-
run Sinatra::Application
|
12
|
+
run Sinatra::Application
|
data/lib/oauthio/access_token.rb
CHANGED
@@ -9,23 +9,24 @@ module Oauthio
|
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
provider = hash.delete('provider') || hash.delete(:provider)
|
13
|
+
access_token = hash.delete('access_token') || hash.delete(:access_token)
|
14
|
+
oauth_token = hash.delete('oauth_token') || hash.delete(:oauth_token)
|
15
|
+
oauth_token_secret = hash.delete('oauth_token_secret') ||
|
16
|
+
hash.delete(:oauth_token_secret)
|
17
|
+
new(client, provider, access_token, oauth_token, oauth_token_secret,
|
17
18
|
hash)
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
21
|
-
def initialize(client, provider, token, oauth_token,
|
22
|
+
def initialize(client, provider, token, oauth_token, oauth_secret, opts={})
|
22
23
|
super client, token, opts
|
23
24
|
@provider = provider
|
24
25
|
@oauth_token = oauth_token.to_s
|
25
|
-
@oauth_token_secret =
|
26
|
+
@oauth_token_secret = oauth_secret.to_s
|
26
27
|
end
|
27
28
|
|
28
|
-
def me
|
29
|
+
def me
|
29
30
|
k = @client.id
|
30
31
|
# oauthv = 1 # TODO: Update this
|
31
32
|
|
@@ -34,13 +35,14 @@ module Oauthio
|
|
34
35
|
oauthio_header = "k=#{k}&access_token=#{@token}"
|
35
36
|
elsif !@oauth_token.empty? && !@oauth_token_secret.empty?
|
36
37
|
# oauthv=#{oauthv}
|
37
|
-
oauthio_header = "k=#{k}&oauth_token=#{@oauth_token}&
|
38
|
+
oauthio_header = "k=#{k}&oauth_token=#{@oauth_token}&" +
|
39
|
+
"oauth_token_secret=#{@oauth_token_secret}"
|
38
40
|
else
|
39
41
|
# TODO: Throw error if no tokens found
|
40
42
|
end
|
41
|
-
opts = {headers
|
43
|
+
opts = {:headers => {:oauthio => oauthio_header}}
|
42
44
|
me_url = client.me_url(provider)
|
43
45
|
request(:get, me_url, opts)
|
44
46
|
end
|
45
47
|
end
|
46
|
-
end
|
48
|
+
end
|
data/lib/oauthio/client.rb
CHANGED
@@ -17,7 +17,7 @@ module Oauthio
|
|
17
17
|
# @option opts [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error
|
18
18
|
# on responses with 400+ status codes
|
19
19
|
# @yield [builder] The Faraday connection builder
|
20
|
-
def initialize(client_id, client_secret, opts
|
20
|
+
def initialize(client_id, client_secret, opts={}, &block)
|
21
21
|
_opts = opts.dup
|
22
22
|
@id = client_id
|
23
23
|
@secret = client_secret
|
@@ -35,15 +35,17 @@ module Oauthio
|
|
35
35
|
@options[:connection_opts][:ssl] = ssl if ssl
|
36
36
|
end
|
37
37
|
|
38
|
-
def me_url(provider, params
|
39
|
-
connection.build_url(options[:me_url].sub(/:provider/, provider), params).
|
38
|
+
def me_url(provider, params=nil)
|
39
|
+
connection.build_url(options[:me_url].sub(/:provider/, provider), params).
|
40
|
+
to_s
|
40
41
|
end
|
41
42
|
|
42
43
|
# The authorize endpoint URL of the OAuth2 provider
|
43
44
|
#
|
44
45
|
# @param [Hash] params additional query parameters
|
45
|
-
def authorize_url(provider, params
|
46
|
-
connection.build_url(options[:authorize_url].sub(/:provider/, provider),
|
46
|
+
def authorize_url(provider, params=nil)
|
47
|
+
connection.build_url(options[:authorize_url].sub(/:provider/, provider),
|
48
|
+
params).to_s
|
47
49
|
end
|
48
50
|
|
49
51
|
# Makes a request relative to the specified site root.
|
@@ -58,12 +60,13 @@ module Oauthio
|
|
58
60
|
# code response for this request. Will default to client option
|
59
61
|
# @option opts [Symbol] :parse @see Response::initialize
|
60
62
|
# @yield [req] The Faraday request
|
61
|
-
def request(verb, url, opts
|
63
|
+
def request(verb, url, opts={}) # rubocop:disable CyclomaticComplexity, MethodLength
|
62
64
|
url = connection.build_url(url, opts[:params]).to_s
|
63
65
|
|
64
|
-
response =
|
65
|
-
|
66
|
-
|
66
|
+
response =
|
67
|
+
connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
|
68
|
+
yield(req) if block_given?
|
69
|
+
end
|
67
70
|
|
68
71
|
# Only really care about the status and the actual return body.
|
69
72
|
# Oauth2 strategy wraps the response in a Response object that handles parsing and whatnot. That is great when
|
@@ -74,29 +77,29 @@ module Oauthio
|
|
74
77
|
response = JSON.parse(response.body)
|
75
78
|
response['status'] = status
|
76
79
|
response['headers'] = headers
|
77
|
-
response = Hashie::Mash.new
|
80
|
+
response = Hashie::Mash.new(response)
|
78
81
|
|
79
82
|
case response.status
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
83
|
+
when 301, 302, 303, 307
|
84
|
+
opts[:redirect_count] ||= 0
|
85
|
+
opts[:redirect_count] += 1
|
86
|
+
return response if opts[:redirect_count] > options[:max_redirects]
|
87
|
+
if response.status == 303
|
88
|
+
verb = :get
|
89
|
+
opts.delete(:body)
|
90
|
+
end
|
91
|
+
request(verb, response.headers['location'], opts)
|
92
|
+
when 200..299, 300..399
|
93
|
+
# on non-redirecting 3xx statuses, just return the response
|
94
|
+
response
|
95
|
+
when 400..599
|
96
|
+
error = OAuth2::Error.new(response)
|
97
|
+
fail(error) if opts.fetch(:raise_errors, options[:raise_errors])
|
98
|
+
response.error = error
|
99
|
+
response
|
100
|
+
else
|
101
|
+
error = OAuth2::Error.new(response)
|
102
|
+
fail(error, "Unhandled status code value of #{response.status}")
|
100
103
|
end
|
101
104
|
end
|
102
105
|
|
@@ -106,12 +109,13 @@ module Oauthio
|
|
106
109
|
# @param [Hash] access token options, to pass to the AccessToken object
|
107
110
|
# @param [Class] class of access token for easier subclassing OAuth2::AccessToken
|
108
111
|
# @return [AccessToken] the initalized AccessToken
|
109
|
-
def get_token(params, access_token_opts
|
110
|
-
opts = {:raise_errors => options[:raise_errors],
|
112
|
+
def get_token(params, access_token_opts={}, access_token_class=AccessToken)
|
113
|
+
opts = {:raise_errors => options[:raise_errors],
|
114
|
+
:parse => params.delete(:parse)}
|
111
115
|
if options[:token_method] == :post
|
112
116
|
headers = params.delete(:headers)
|
113
117
|
opts[:body] = params
|
114
|
-
opts[:headers] =
|
118
|
+
opts[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'}
|
115
119
|
opts[:headers].merge!(headers) if headers
|
116
120
|
else
|
117
121
|
opts[:params] = params
|
@@ -120,14 +124,16 @@ module Oauthio
|
|
120
124
|
|
121
125
|
# Verify state in the response matches the one in the session
|
122
126
|
if response.state != @state
|
123
|
-
raise ::OmniAuth::Strategies::OAuth2::CallbackError.new(nil,
|
127
|
+
raise ::OmniAuth::Strategies::OAuth2::CallbackError.new(nil,
|
128
|
+
:csrf_detected)
|
124
129
|
end
|
125
130
|
|
126
131
|
# error = Error.new(response)
|
127
132
|
# fail(error) if options[:raise_errors] && !(response.parsed.is_a?(Hash) && response.parsed['access_token'])
|
128
133
|
|
129
|
-
provider_client = ::Oauthio::Client.new(@id, @secret, {
|
130
|
-
access_token_class.from_hash(provider_client,
|
134
|
+
provider_client = ::Oauthio::Client.new(@id, @secret, {:site => @site})
|
135
|
+
access_token_class.from_hash(provider_client,
|
136
|
+
response.merge(access_token_opts))
|
131
137
|
end
|
132
138
|
|
133
139
|
# The Authorization Code strategy
|
@@ -162,4 +168,4 @@ module Oauthio
|
|
162
168
|
@assertion ||= OAuth2::Strategy::Assertion.new(self)
|
163
169
|
end
|
164
170
|
end
|
165
|
-
end
|
171
|
+
end
|
@@ -18,21 +18,19 @@ module Oauthio
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def info
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
'birthdate' => _raw_info['birthdate'],
|
35
|
-
})
|
21
|
+
# Map OAuth.io info to standard OmniAuth keys, e.g., OAuth.io's alias
|
22
|
+
# becomes nickname.
|
23
|
+
# See https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema
|
24
|
+
prune!({'nickname' => _raw_info['alias'],
|
25
|
+
'description' => _raw_info['bio'],
|
26
|
+
'image' => _raw_info['avatar'],
|
27
|
+
'first_name' => _raw_info['firstname'],
|
28
|
+
'last_name' => _raw_info['lastname'],
|
29
|
+
'phone' => _raw_info['phones'],
|
30
|
+
'email' => _raw_info['email'],
|
31
|
+
'name' => _raw_info['name'],
|
32
|
+
'location' => _raw_info['location'],
|
33
|
+
'urls' => _raw_info['urls']})
|
36
34
|
end
|
37
35
|
|
38
36
|
def extra
|
@@ -47,7 +45,7 @@ module Oauthio
|
|
47
45
|
end
|
48
46
|
|
49
47
|
def raw_info
|
50
|
-
@raw_info ||= _raw_info['raw']
|
48
|
+
@raw_info ||= _raw_info['raw'] || {}
|
51
49
|
end
|
52
50
|
|
53
51
|
def info_options
|
@@ -64,11 +62,21 @@ module Oauthio
|
|
64
62
|
|
65
63
|
def credentials
|
66
64
|
hash = {}
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
65
|
+
unless @access_token.token.empty?
|
66
|
+
hash.merge!('token' => @access_token.token)
|
67
|
+
end
|
68
|
+
has_oauth_token = !@access_token.oauth_token.empty?
|
69
|
+
has_oauth_token_secret = !@access_token.oauth_token_secret.empty?
|
70
|
+
if has_oauth_token && has_oauth_token_secret
|
71
|
+
hash.merge!('oauth_token' => @access_token.oauth_token,
|
72
|
+
'oauth_token_secret' => @access_token.oauth_token_secret)
|
73
|
+
end
|
74
|
+
if @access_token.expires? && @access_token.refresh_token
|
75
|
+
hash.merge!('refresh_token' => @access_token.refresh_token)
|
76
|
+
end
|
77
|
+
if @access_token.expires?
|
78
|
+
hash.merge!('expires_at' => @access_token.expires_at)
|
79
|
+
end
|
72
80
|
hash.merge!('expires' => @access_token.expires?)
|
73
81
|
hash
|
74
82
|
end
|
data/lib/omniauth/oauthio.rb
CHANGED
@@ -13,36 +13,44 @@ module OmniAuth
|
|
13
13
|
args [:client_id, :client_secret]
|
14
14
|
|
15
15
|
# Give your strategy a name.
|
16
|
-
option :name,
|
16
|
+
option :name, 'oauthio'
|
17
17
|
|
18
18
|
# This is where you pass the options you would pass when
|
19
19
|
# initializing your consumer from the OAuth gem.
|
20
|
-
option :client_options, {
|
21
|
-
:site => 'https://oauth.io',
|
22
|
-
}
|
20
|
+
option :client_options, {:site => 'https://oauth.io'}
|
23
21
|
|
24
22
|
option :client_id, nil
|
25
23
|
option :client_secret, nil
|
26
24
|
|
27
25
|
def current_path
|
28
|
-
# This might not be completely safe. I want to ensure that the
|
29
|
-
# so the .json should be
|
30
|
-
|
26
|
+
# This might not be completely safe. I want to ensure that the
|
27
|
+
# current_path does not have a format at the end so the .json should be
|
28
|
+
# removed.
|
29
|
+
super.sub(/(\.json)$/, '')
|
31
30
|
end
|
32
31
|
|
33
32
|
def sub_provider
|
33
|
+
# e.g., /auth/oauthio/twitter
|
34
34
|
after_base = request.path.split("#{path_prefix}/#{name}/").last
|
35
35
|
slashes = after_base.split('/')
|
36
|
-
slashes.length > 1 ? slashes.first
|
36
|
+
str = slashes.length > 1 ? slashes.first : after_base
|
37
|
+
str.split('.').first
|
37
38
|
end
|
38
39
|
|
39
40
|
def request_path
|
40
|
-
options[:request_path].is_a?(String)
|
41
|
+
if (path=options[:request_path]).is_a?(String)
|
42
|
+
path
|
43
|
+
else
|
44
|
+
"#{path_prefix}/#{name}/#{sub_provider}"
|
45
|
+
end
|
41
46
|
end
|
42
47
|
|
43
48
|
def callback_path
|
44
|
-
|
45
|
-
path
|
49
|
+
callback_option = options[:callback_path]
|
50
|
+
path = callback_option if callback_option.is_a?(String)
|
51
|
+
if callback_option.respond_to?(:call) && callback_option.call(env)
|
52
|
+
path ||= current_path
|
53
|
+
end
|
46
54
|
path ||= custom_path(:request_path)
|
47
55
|
path ||= "#{path_prefix}/#{name}/#{sub_provider}/callback"
|
48
56
|
path
|
@@ -52,25 +60,20 @@ module OmniAuth
|
|
52
60
|
params = authorize_params
|
53
61
|
provider = sub_provider
|
54
62
|
|
55
|
-
opts = {
|
56
|
-
state: params.state
|
57
|
-
}.to_json
|
63
|
+
opts = {:state => params.state}.to_json
|
58
64
|
|
59
|
-
# We may want to skip redirecting the user if calling from a
|
65
|
+
# We may want to skip redirecting the user if calling from a
|
66
|
+
# single-page application that does not want to reload the page.
|
60
67
|
if request.path_info =~ /\.json$/
|
61
|
-
return Rack::Response.new(opts, 200,
|
68
|
+
return Rack::Response.new(opts, 200,
|
69
|
+
'content-type' => 'application/json').finish
|
62
70
|
end
|
63
71
|
|
64
|
-
|
72
|
+
defaults = {:redirect_uri => callback_url_with_state(params.state)}
|
73
|
+
options = defaults.merge({opts: opts})
|
74
|
+
redirect client.auth_code.authorize_url(provider, options)
|
65
75
|
end
|
66
76
|
|
67
|
-
|
68
|
-
# note: the callback phase should be the same regardless!
|
69
|
-
#
|
70
|
-
# The request phase though needs to have multiple options
|
71
|
-
# 1. take care of everything the js-sdk does.
|
72
|
-
# 2. partial control where we can get the state to pass to the js-sdk.
|
73
|
-
|
74
77
|
def callback_url_with_state(state)
|
75
78
|
uri = URI.parse(callback_url)
|
76
79
|
new_query_ar = URI.decode_www_form(uri.query || '') << ['state', state]
|
@@ -79,43 +82,64 @@ module OmniAuth
|
|
79
82
|
end
|
80
83
|
|
81
84
|
def auth_hash
|
82
|
-
provider_info = ::Oauthio::Providers::Oauthio.new(access_token,
|
85
|
+
provider_info = ::Oauthio::Providers::Oauthio.new(access_token,
|
86
|
+
client.secret,
|
87
|
+
options)
|
83
88
|
provider = access_token.provider
|
84
89
|
hash = AuthHash.new(:provider => provider, :uid => provider_info.uid)
|
85
90
|
hash.info = provider_info.info unless provider_info.skip_info?
|
86
|
-
|
91
|
+
if provider_info.credentials
|
92
|
+
hash.credentials = provider_info.credentials
|
93
|
+
end
|
87
94
|
hash.extra = provider_info.extra if provider_info.extra
|
88
95
|
hash
|
89
96
|
end
|
90
97
|
|
91
98
|
def callback_phase
|
92
|
-
|
93
|
-
# TODO: Is there an option we can pass to OAuth.io to prevent it from
|
94
|
-
#
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
99
|
+
unless request.params['code']
|
100
|
+
# TODO: Is there an option we can pass to OAuth.io to prevent it from
|
101
|
+
# putting the code in the hash part of the url? Currently we have to
|
102
|
+
# parse the hash to get the code and then do an additional redirect.
|
103
|
+
html = <<-END.gsub(/^\s{10}/, '')
|
104
|
+
<!DOCTYPE html>
|
105
|
+
<html>
|
106
|
+
<head>
|
107
|
+
<script>
|
108
|
+
(function() {
|
109
|
+
"use strict";
|
110
|
+
var hash = document.location.hash;
|
111
|
+
var data = JSON.parse(decodeURIComponent(hash.split("=")[1]));
|
112
|
+
var code = data.data.code;
|
113
|
+
document.location.href = document.location.origin + document.location.pathname + document.location.search + "&code=" + code;
|
114
|
+
})();
|
115
|
+
</script>
|
116
|
+
</head>
|
117
|
+
<body></body>
|
118
|
+
</html>
|
119
|
+
END
|
104
120
|
return Rack::Response.new(html, 200).finish
|
105
121
|
end
|
106
122
|
|
107
|
-
|
108
|
-
|
109
|
-
|
123
|
+
error_message = request.params['error_reason'] ||
|
124
|
+
request.params['error']
|
125
|
+
if error_message
|
126
|
+
error_description = request.params['error_description'] ||
|
127
|
+
request.params['error_reason']
|
128
|
+
error = CallbackError.new(request.params['error'],
|
129
|
+
error_description,
|
130
|
+
request.params['error_uri'])
|
131
|
+
fail!(error_message, error)
|
110
132
|
elsif !options.provider_ignores_state && !verified_state?
|
111
|
-
|
133
|
+
error = CallbackError.new(:csrf_detected, 'CSRF detected')
|
134
|
+
fail!(:csrf_detected, error)
|
112
135
|
else
|
113
136
|
self.access_token = build_access_token
|
114
137
|
self.access_token = access_token.refresh! if access_token.expired?
|
115
|
-
|
116
138
|
env['omniauth.auth'] = auth_hash
|
139
|
+
|
117
140
|
# Delete the omniauth.state after we have verified all requests
|
118
141
|
session.delete('omniauth.state')
|
142
|
+
|
119
143
|
call_app!
|
120
144
|
end
|
121
145
|
rescue CallbackError => e
|
@@ -129,10 +153,12 @@ module OmniAuth
|
|
129
153
|
end
|
130
154
|
|
131
155
|
protected
|
156
|
+
|
132
157
|
def client
|
133
158
|
state = session['omniauth.state']
|
134
159
|
options.client_options[:state] = state
|
135
|
-
::Oauthio::Client.new(options.client_id, options.client_secret,
|
160
|
+
::Oauthio::Client.new(options.client_id, options.client_secret,
|
161
|
+
deep_symbolize(options.client_options))
|
136
162
|
end
|
137
163
|
|
138
164
|
def verified_state?
|
data/omniauth-oauthio.gemspec
CHANGED
@@ -17,7 +17,8 @@ Gem::Specification.new do |s|
|
|
17
17
|
|
18
18
|
s.add_runtime_dependency 'omniauth-oauth2', '~> 1.2'
|
19
19
|
|
20
|
-
s.add_development_dependency 'minitest'
|
21
20
|
s.add_development_dependency 'mocha'
|
22
21
|
s.add_development_dependency 'rake'
|
22
|
+
s.add_development_dependency 'rspec'
|
23
|
+
s.add_development_dependency 'simplecov'
|
23
24
|
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
RSpec.describe Oauthio::Client do
|
2
|
+
let(:site) { 'https://oauth.io' }
|
3
|
+
let(:client_id) { '123' }
|
4
|
+
let(:client_secret) { '53cr37' }
|
5
|
+
let(:client) { Oauthio::Client.new(client_id, client_secret, options) }
|
6
|
+
|
7
|
+
describe 'me_url' do
|
8
|
+
let(:provider) { 'google' }
|
9
|
+
subject { client.me_url(provider, params) }
|
10
|
+
|
11
|
+
context 'when me_url option is not specified' do
|
12
|
+
let(:options) { {:site => site} }
|
13
|
+
|
14
|
+
context 'without URL parameters' do
|
15
|
+
let(:params) { {} }
|
16
|
+
|
17
|
+
it 'replaces provider in URL' do
|
18
|
+
expect(subject).to eq("#{site}/auth/#{provider}/me")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with URL parameters' do
|
23
|
+
let(:params) { {'foo' => 'bar', 'cat' => 'dog'} }
|
24
|
+
|
25
|
+
it 'includes given URL parameters' do
|
26
|
+
expect(subject).to eq("#{site}/auth/#{provider}/me?cat=dog&foo=bar")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when me_url option is specified' do
|
32
|
+
let(:options) {
|
33
|
+
{:site => site, :me_url => '/big-cats/catch-mice/:provider'}
|
34
|
+
}
|
35
|
+
|
36
|
+
context 'without URL parameters' do
|
37
|
+
let(:params) { {} }
|
38
|
+
|
39
|
+
it 'replaces provider in URL' do
|
40
|
+
expect(subject).to eq("#{site}/big-cats/catch-mice/#{provider}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'with URL parameters' do
|
45
|
+
let(:params) { {'foo' => 'bar', 'cat' => 'dog'} }
|
46
|
+
|
47
|
+
it 'includes given URL parameters' do
|
48
|
+
expect(subject).
|
49
|
+
to eq("#{site}/big-cats/catch-mice/#{provider}?cat=dog&foo=bar")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'authorize_url' do
|
56
|
+
let(:provider) { 'twitter' }
|
57
|
+
subject { client.authorize_url(provider, params) }
|
58
|
+
|
59
|
+
context 'when authorize_url option is not specified' do
|
60
|
+
let(:options) { {:site => site} }
|
61
|
+
|
62
|
+
context 'without URL parameters' do
|
63
|
+
let(:params) { {} }
|
64
|
+
|
65
|
+
it 'replaces provider in URL' do
|
66
|
+
expect(subject).to eq("#{site}/auth/#{provider}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'with URL parameters' do
|
71
|
+
let(:params) { {'foo' => 'bar', 'cat' => 'dog'} }
|
72
|
+
|
73
|
+
it 'includes given URL parameters' do
|
74
|
+
expect(subject).to eq("#{site}/auth/#{provider}?cat=dog&foo=bar")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'when authorize_url option is specified' do
|
80
|
+
let(:options) {
|
81
|
+
{:site => site, :authorize_url => '/:provider/FABULOUS-URL'}
|
82
|
+
}
|
83
|
+
|
84
|
+
context 'without URL parameters' do
|
85
|
+
let(:params) { {} }
|
86
|
+
|
87
|
+
it 'replaces provider in URL' do
|
88
|
+
expect(subject).to eq("#{site}/#{provider}/FABULOUS-URL")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'with URL parameters' do
|
93
|
+
let(:params) { {'foo1' => 'bar', 'cat' => 'dog2'} }
|
94
|
+
|
95
|
+
it 'includes given URL parameters' do
|
96
|
+
expect(subject).
|
97
|
+
to eq("#{site}/#{provider}/FABULOUS-URL?cat=dog2&foo1=bar")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
RSpec.describe OmniAuth::Strategies::Oauthio do
|
2
|
+
let(:provider) { 'facebook' }
|
3
|
+
let(:scheme) { 'https' }
|
4
|
+
let(:path) { "/auth/oauthio/#{provider}" }
|
5
|
+
let(:url) { "#{scheme}://example.com#{path}" }
|
6
|
+
let(:client_id) { '123' }
|
7
|
+
let(:client_secret) { '53cr37' }
|
8
|
+
let(:options) { {} }
|
9
|
+
let(:raw_info) {
|
10
|
+
{'id' => '1234567', 'email' => 'j.doe@example.com', 'name' => 'Jane Doe',
|
11
|
+
'gender' => 'female'}
|
12
|
+
}
|
13
|
+
let(:env) {
|
14
|
+
OmniAuth::AuthHash.new({
|
15
|
+
'omniauth.auth' => {
|
16
|
+
'provider' => provider,
|
17
|
+
'uid' => '1234',
|
18
|
+
'credentials' => {'token' => 'abcdefg'},
|
19
|
+
'extra' => {'raw_info' => raw_info}
|
20
|
+
},
|
21
|
+
'REQUEST_METHOD' => 'GET',
|
22
|
+
'PATH_INFO' => path,
|
23
|
+
'rack.session' => {},
|
24
|
+
'rack.input' => StringIO.new('test=true')
|
25
|
+
})
|
26
|
+
}
|
27
|
+
let(:request) {
|
28
|
+
mock = double('Request', :params => {}, :cookies => {}, :env => env)
|
29
|
+
allow(mock).to receive(:path).and_return(path)
|
30
|
+
allow(mock).to receive(:path_info).and_return(path)
|
31
|
+
allow(mock).to receive(:scheme).and_return(scheme)
|
32
|
+
allow(mock).to receive(:url).and_return(url)
|
33
|
+
mock
|
34
|
+
}
|
35
|
+
let(:app) { ->(env) { [200, {}, ['Hello.']] } }
|
36
|
+
subject {
|
37
|
+
args = [app, client_id, client_secret, options]
|
38
|
+
OmniAuth::Strategies::Oauthio.new(*args).tap do |strategy|
|
39
|
+
allow(strategy).to receive(:request) { request }
|
40
|
+
end
|
41
|
+
}
|
42
|
+
|
43
|
+
before { OmniAuth.config.test_mode = true }
|
44
|
+
after { OmniAuth.config.test_mode = false }
|
45
|
+
|
46
|
+
describe 'sub_provider' do
|
47
|
+
it 'extracts the OAuth provider from the request path' do
|
48
|
+
expect(subject.sub_provider).to eq(provider)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'request_path' do
|
53
|
+
context 'without request_path option' do
|
54
|
+
it 'returns path for provider' do
|
55
|
+
expect(subject.request_path).to eq("/auth/oauthio/#{provider}")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'with request_path option' do
|
60
|
+
let(:options) { {:request_path => '/authentication/oauthio'} }
|
61
|
+
|
62
|
+
it 'returns specified request_path' do
|
63
|
+
expect(subject.request_path).to eq(options[:request_path])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'callback_path' do
|
69
|
+
context 'without callback_path or request_path options' do
|
70
|
+
it 'returns a callback URL for the provider' do
|
71
|
+
expect(subject.callback_path).
|
72
|
+
to eq("/auth/oauthio/#{provider}/callback")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'with string request_path option' do
|
77
|
+
let(:options) { {:request_path => '/some/neat/url'} }
|
78
|
+
|
79
|
+
it 'returns specified request_path' do
|
80
|
+
expect(subject.callback_path).to eq(options[:request_path])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'with lambda request_path option' do
|
85
|
+
let(:options) { {:request_path => ->(env) { '/really/cool' }} }
|
86
|
+
|
87
|
+
it 'returns result of specified request_path function' do
|
88
|
+
expect(subject.callback_path).to eq('/really/cool')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'with lambda callback_path option' do
|
93
|
+
let(:options) { {:callback_path => ->(env) { 'something truthy' }} }
|
94
|
+
|
95
|
+
it 'returns current path' do
|
96
|
+
expect(subject.callback_path).to eq(path)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'with string callback_path option' do
|
101
|
+
let(:options) { {:callback_path => "/users/auth/#{provider}/callback"} }
|
102
|
+
|
103
|
+
it 'returns specified callback_path' do
|
104
|
+
expect(subject.callback_path).to eq(options[:callback_path])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'callback_url_with_state' do
|
110
|
+
let(:state) { 'cool' }
|
111
|
+
before { subject.call!(env) }
|
112
|
+
|
113
|
+
it 'returns full URL with specified state' do
|
114
|
+
expect(subject.callback_url_with_state(state)).
|
115
|
+
to eq("#{url}/callback?state=#{state}")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe 'auth_hash' do
|
120
|
+
let(:client) { Oauthio::Client.new(client_id, client_secret, {}) }
|
121
|
+
let(:access_token) {
|
122
|
+
token = Oauthio::AccessToken.from_hash(client, {:provider => provider})
|
123
|
+
allow(token).to receive(:request).and_return({'data' => raw_info})
|
124
|
+
token
|
125
|
+
}
|
126
|
+
before do
|
127
|
+
subject.call!(env)
|
128
|
+
allow(subject).to receive(:access_token).and_return(access_token)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'returns a hash' do
|
132
|
+
expect(subject.auth_hash).to be_an_instance_of(OmniAuth::AuthHash)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'includes specified provider' do
|
136
|
+
expect(subject.auth_hash['provider']).to eq(provider)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'has user info from provider' do
|
140
|
+
expect(subject.auth_hash.info['name']).to eq(raw_info['name'])
|
141
|
+
expect(subject.auth_hash.info['email']).to eq(raw_info['email'])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
ENV['RACK_ENV'] = 'test'
|
2
|
+
|
3
|
+
require 'simplecov'
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
require 'omniauth-oauthio'
|
7
|
+
|
8
|
+
Dir[File.expand_path('../support/**/*', __FILE__)].each {|f| require f }
|
9
|
+
|
10
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.filter_run :focus
|
13
|
+
config.run_all_when_everything_filtered = true
|
14
|
+
|
15
|
+
if config.files_to_run.one?
|
16
|
+
config.default_formatter = 'doc'
|
17
|
+
end
|
18
|
+
|
19
|
+
config.order = :random
|
20
|
+
Kernel.srand config.seed
|
21
|
+
|
22
|
+
config.expect_with :rspec do |expectations|
|
23
|
+
expectations.syntax = :expect
|
24
|
+
end
|
25
|
+
|
26
|
+
config.mock_with :rspec do |mocks|
|
27
|
+
mocks.syntax = :expect
|
28
|
+
mocks.verify_partial_doubles = true
|
29
|
+
end
|
30
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniauth-oauthio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Rowlands
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: omniauth-oauth2
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: mocha
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -53,7 +53,21 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - ">="
|
@@ -74,12 +88,12 @@ extensions: []
|
|
74
88
|
extra_rdoc_files: []
|
75
89
|
files:
|
76
90
|
- ".gitignore"
|
91
|
+
- ".rspec"
|
77
92
|
- Gemfile
|
78
93
|
- LICENSE
|
79
94
|
- README.md
|
80
95
|
- Rakefile
|
81
96
|
- example/Gemfile
|
82
|
-
- example/Gemfile.lock
|
83
97
|
- example/app.rb
|
84
98
|
- example/config.ru
|
85
99
|
- lib/oauthio/access_token.rb
|
@@ -91,6 +105,9 @@ files:
|
|
91
105
|
- lib/omniauth/oauthio/version.rb
|
92
106
|
- lib/omniauth/strategies/oauthio.rb
|
93
107
|
- omniauth-oauthio.gemspec
|
108
|
+
- spec/client_spec.rb
|
109
|
+
- spec/oauthio_spec.rb
|
110
|
+
- spec/spec_helper.rb
|
94
111
|
- test/helper.rb
|
95
112
|
- test/support/shared_examples.rb
|
96
113
|
- test/test.rb
|
@@ -119,6 +136,9 @@ signing_key:
|
|
119
136
|
specification_version: 4
|
120
137
|
summary: OAuth.io Strategy for OmniAuth
|
121
138
|
test_files:
|
139
|
+
- spec/client_spec.rb
|
140
|
+
- spec/oauthio_spec.rb
|
141
|
+
- spec/spec_helper.rb
|
122
142
|
- test/helper.rb
|
123
143
|
- test/support/shared_examples.rb
|
124
144
|
- test/test.rb
|