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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d37a4503efe522eb66a84b32a32711757aae4d2
4
- data.tar.gz: e8befcc5493e673f02ab3284d74c489357206e29
3
+ metadata.gz: 70606edfa2af2716b721133a36072b165f60c210
4
+ data.tar.gz: 4be6085639515da6d8d873369b6af0b6f838a55a
5
5
  SHA512:
6
- metadata.gz: 72832698e7031e680ba868d66958034abafd4c202b72ff7d41c55edc5f91540baa65c7089d11bca36be7e363557c42367a786d900955c095b4333a894b416e37
7
- data.tar.gz: aa6173e79c1fcf85ea5abb981bd068e3db13ad7a2d6f62ec32ba0e28b732db93463e60e9e85ff40276d28300f75a5d95913555c924ef204cd5eaa48d7fd4ee0f
6
+ metadata.gz: 321dd9cb4655504e3c663bef40d2858aba636f6b61e9b67223e836795191fe2c8ba28f9f75a0734a65071e65532efc53acea399eaea1b479e112287c5f61a911
7
+ data.tar.gz: 6b44d837670e892a6e89e854e1f6433f98145a3ead21250af3c61972aedbb581cad8cad0f97f97d41db77ef16bc9139f2d3e68559d95ffa4a5e4dece865fc8a4
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  *.gem
2
2
  .bundle
3
- .rspec
4
3
  /Gemfile.lock
5
4
  example/Gemfile.lock
6
5
  pkg/*
@@ -9,3 +8,5 @@ tmp
9
8
  bin
10
9
  .idea
11
10
  *.iml
11
+ example/app.log
12
+ /coverage
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --require spec_helper
3
+ --format doc
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', path: 'https://github.com/jgrowl/omniauth-oauthio.git'
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 get request to initiate the request_phase, using the .json option for SPA (This is to get a state string from the server).
35
-
36
- 3. Use OAuth.io's javascript api to initiate a popup (Passing along the state from step 2).
37
-
38
- 4. Perform get request to initiate callback_phase (Passing along the state and code received in step 3).
39
-
40
- For example: (NOTE: I need to update this. I am currently using dart in my test app)
41
-
42
- ```coffeescript
43
- OAuth.initialize('YOUR_PUBLIC_KEY')
44
-
45
- $.get "http://localhost:3000/users/auth/oauthio.json", (data) ->
46
- @options = data
47
-
48
- OAuth.popup provider, @options, (err, res) ->
49
- if (err)
50
- console.log err
51
- else
52
- $.get "http://localhost:3000/users/auth/oauthio/twitter/callback.json?state=@options.state&code=@options.code", (data) ->
53
- console.log(data)
54
- # Perform additional login steps
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
- ### OAuth.io
63
+ ### Rails
60
64
 
61
- Be sure to enable the Server-side (code) option on any providers you want to use with this strategy.
65
+ Set `path_prefix` in `config/initializers/omniauth.rb`:
62
66
 
63
- ### Custom Callback URL/Path
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
- 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.
75
+ #### Devise
66
76
 
67
- ### Devise
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 devise routes in `config/routes.rb`
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 "/users/auth/:provider(/:sub_provider)",
80
- constraints: { provider: /oauthio/ },
81
- to: "users/omniauth_callbacks#passthru",
82
- as: :omniauth_authorize,
83
- via: [:get, :post]
84
-
85
- match "/users/auth/:action(/:sub_provider)/callback",
86
- constraints: { action: /oauthio/, sub_provider: /twitter|google/ },
87
- to: "users/omniauth_callbacks",
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
- # TODO: Do your login logic here! ie. look up the user by the uid or create one if it does not already exist!
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: auth_hash}
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
- Create the method used in your callback in your `user.rb`
117
-
118
- # Understanding server side flow
128
+ ## Understanding server-side flow
119
129
 
120
- oauth.io describes how everything works in their [security](https://oauth.io/docs/security) section.
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
- ## Credit
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
- https://oauth.io/
144
+ Then visit [http://localhost:9292](http://localhost:9292) in your browser.
145
+
146
+ ## Credit
128
147
 
129
- https://github.com/mkdynamic/omniauth-facebook
148
+ - [OAuth.io](https://oauth.io/)
149
+ - [omniauth-facebook](https://github.com/mkdynamic/omniauth-facebook)
data/Rakefile CHANGED
@@ -1,8 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
- require 'rake/testtask'
2
+ require 'rspec/core/rake_task'
3
3
 
4
- Rake::TestTask.new do |task|
5
- task.libs << 'test'
6
- end
4
+ RSpec::Core::RakeTask.new(:spec)
7
5
 
8
- task :default => :test
6
+ task :default => :spec
data/example/Gemfile CHANGED
@@ -1,5 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'sinatra'
4
- gem 'sinatra-reloader'
4
+ gem 'sinatra-contrib'
5
5
  gem 'omniauth-oauthio', :path => '../'
data/example/app.rb CHANGED
@@ -1,5 +1,6 @@
1
+ require 'bundler/setup'
1
2
  require 'sinatra'
2
- require "sinatra/reloader"
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("app.log", "a+")
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 app. The redirect is just here to
19
- # explicit declare this server-side flow.
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,&block)
140
- get(url,&block)
141
- post(url,&block)
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 './app.rb'
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
@@ -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
- new(client,
13
- hash.delete('provider') || hash.delete(:provider),
14
- hash.delete('access_token') || hash.delete(:access_token),
15
- hash.delete('oauth_token') || hash.delete(:oauth_token),
16
- hash.delete('oauth_token_secret') || hash.delete(:oauth_token_secret),
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, oauth_token_secret, opts = {})
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 = oauth_token_secret.to_s
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}&oauth_token_secret=#{@oauth_token_secret}"
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: {oauthio: oauthio_header}}
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
@@ -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 = {}, &block)
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 = nil)
39
- connection.build_url(options[:me_url].sub(/:provider/, provider), params).to_s
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 = nil)
46
- connection.build_url(options[:authorize_url].sub(/:provider/, provider), params).to_s
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 = {}) # rubocop:disable CyclomaticComplexity, MethodLength
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 = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
65
- yield(req) if block_given?
66
- end
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 response
80
+ response = Hashie::Mash.new(response)
78
81
 
79
82
  case response.status
80
- when 301, 302, 303, 307
81
- opts[:redirect_count] ||= 0
82
- opts[:redirect_count] += 1
83
- return response if opts[:redirect_count] > options[:max_redirects]
84
- if response.status == 303
85
- verb = :get
86
- opts.delete(:body)
87
- end
88
- request(verb, response.headers['location'], opts)
89
- when 200..299, 300..399
90
- # on non-redirecting 3xx statuses, just return the response
91
- response
92
- when 400..599
93
- error = OAuth2::Error.new(response)
94
- fail(error) if opts.fetch(:raise_errors, options[:raise_errors])
95
- response.error = error
96
- response
97
- else
98
- error = OAuth2::Error.new(response)
99
- fail(error, "Unhandled status code value of #{response.status}")
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 = {}, access_token_class = AccessToken)
110
- opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)}
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] = {'Content-Type' => 'application/x-www-form-urlencoded'}
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, :csrf_detected);
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, { :site => @site })
130
- access_token_class.from_hash(provider_client, response.merge(access_token_opts))
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
- 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
- })
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
- 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?
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
@@ -37,7 +37,6 @@ module Oauthio
37
37
  params = {'code' => code}.merge(client_params).merge(params)
38
38
  @client.get_token(params, opts)
39
39
  end
40
-
41
40
  end
42
41
  end
43
- end
42
+ end
@@ -5,5 +5,3 @@ require 'oauthio/access_token'
5
5
  require 'oauthio/client'
6
6
  require 'oauthio/strategy/auth_code'
7
7
  require 'oauthio/providers/oauthio'
8
-
9
-
@@ -1,5 +1,5 @@
1
1
  module OmniAuth
2
2
  module Oauthio
3
- VERSION = '0.2.0'
3
+ VERSION = '0.2.1'
4
4
  end
5
5
  end
@@ -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, "oauthio"
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 current_path does not have a format at the end
29
- # so the .json should be removed.
30
- super.sub(/(\.json)$/, '');
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.split('.').first : after_base.split('.').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) ? options[:request_path] : "#{path_prefix}/#{name}/#{sub_provider}"
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
- path = options[:callback_path] if options[:callback_path].is_a?(String)
45
- path ||= current_path if options[:callback_path].respond_to?(:call) && options[:callback_path].call(env)
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 SPA that does not want to reload the page.
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, 'content-type' => 'application/json').finish
68
+ return Rack::Response.new(opts, 200,
69
+ 'content-type' => 'application/json').finish
62
70
  end
63
71
 
64
- redirect client.auth_code.authorize_url(provider, {:redirect_uri => callback_url_with_state(params.state)}.merge({opts: opts}))
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, client.secret, options)
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
- hash.credentials = provider_info.credentials if provider_info.credentials
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
- if !request.params['code']
93
- # TODO: Is there an option we can pass to OAuth.io to prevent it from putting the code in the hash part of the url?
94
- # Currently we to parse the hash to get the code and then do an additional redirect.
95
- html = '<!DOCTYPE html>
96
- <html><head><script>(function() {
97
- "use strict";
98
- var hash = document.location.hash;
99
- var data = JSON.parse(decodeURIComponent(hash.split("=")[1]));
100
- var code = data.data.code
101
- document.location.href = document.location.origin + document.location.pathname + document.location.search + "&code=" + code
102
- //document.location.href = document.location.href + "&code=" + code
103
- })();</script></head><body></body></html>'
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
- error = request.params['error_reason'] || request.params['error']
108
- if error
109
- fail!(error, CallbackError.new(request.params['error'], request.params['error_description'] || request.params['error_reason'], request.params['error_uri']))
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
- fail!(:csrf_detected, CallbackError.new(:csrf_detected, 'CSRF detected'))
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, deep_symbolize(options.client_options))
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?
@@ -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
@@ -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
@@ -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.0
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-15 00:00:00.000000000 Z
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: minitest
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: mocha
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: rake
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