omniauth-withings-oauth2 1.0.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +6 -4
- data/README.md +3 -2
- data/lib/omniauth/strategies/withings.rb +18 -1
- data/lib/omniauth/withings_oauth2/access_token.rb +224 -0
- data/lib/omniauth-withings-oauth2/version.rb +1 -1
- data/lib/omniauth-withings-oauth2.rb +1 -0
- data/omniauth-withings-oauth2.gemspec +1 -1
- data/spec/omniauth/strategies/withings_spec.rb +1 -1
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 571d67518b4fd8a7631b96c81da0abbd9ac3481073eda3f288ca3d1644db867d
|
4
|
+
data.tar.gz: 4d1ef9a1b84dadb3c65121b5e65a40454dbc240500cf64004eabb2eea41232d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 21ab52483c21f96dce91413e47cb82f938bb714451a7d6686c3ff9dc5884c671c5b46cf3f88e717872c05496e1f658c8ea9b4c6ee1598aa1b782964f073db1ab
|
7
|
+
data.tar.gz: a781d25bffaaccf63e9f4de231db775402893dece03f63b7a28c7121ac9b001de1110b05b8e736902fd3e3af9dafe4d2d694f92043eda112870bd3ffd6229f8a
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# OmniAuth Withings OAuth2 Strategy
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/bartimaeus/omniauth-withings-oauth2.svg?branch=master)](https://travis-ci.org/bartimaeus/omniauth-withings-oauth2)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/omniauth-withings-oauth2.svg)](https://badge.fury.io/rb/omniauth-withings-oauth2)
|
4
5
|
|
5
6
|
A Withings OAuth2 strategy for OmniAuth.
|
6
7
|
|
@@ -28,7 +29,7 @@ This is an example that you might put into a Rails initializer at `config/initia
|
|
28
29
|
|
29
30
|
```ruby
|
30
31
|
Rails.application.config.middleware.use OmniAuth::Builder do
|
31
|
-
provider :withings, ENV['WITHINGS_CLIENT_ID'], ENV['WITHINGS_CLIENT_SECRET'], :scope => 'user.info
|
32
|
+
provider :withings, ENV['WITHINGS_CLIENT_ID'], ENV['WITHINGS_CLIENT_SECRET'], :scope => 'user.info,user.metrics'
|
32
33
|
end
|
33
34
|
```
|
34
35
|
|
@@ -42,7 +43,7 @@ For more details, read the withings documentation: http://developer.withings.com
|
|
42
43
|
You can configure the scope option:
|
43
44
|
|
44
45
|
```ruby
|
45
|
-
provider :withings, ENV['WITHINGS_CLIENT_ID'], ENV['WITHINGS_CLIENT_SECRET'], :scope => 'user.info
|
46
|
+
provider :withings, ENV['WITHINGS_CLIENT_ID'], ENV['WITHINGS_CLIENT_SECRET'], :scope => 'user.info,user.metrics,user.activity'
|
46
47
|
```
|
47
48
|
|
48
49
|
## Contributing
|
@@ -8,7 +8,9 @@ module OmniAuth
|
|
8
8
|
option :client_options, {
|
9
9
|
:site => 'https://account.withings.com',
|
10
10
|
:authorize_url => 'https://account.withings.com/oauth2_user/authorize2',
|
11
|
-
:token_url => 'https://
|
11
|
+
:token_url => 'https://wbsapi.withings.net/v2/oauth2',
|
12
|
+
:auth_scheme => :request_body,
|
13
|
+
:access_token_class => OmniAuth::WithingsOauth2::AccessToken
|
12
14
|
}
|
13
15
|
|
14
16
|
option :scope, 'user.info'
|
@@ -27,6 +29,21 @@ module OmniAuth
|
|
27
29
|
full_host + script_name + callback_path
|
28
30
|
end
|
29
31
|
|
32
|
+
def build_access_token
|
33
|
+
verifier = request.params["code"]
|
34
|
+
response = client.auth_code.get_token(
|
35
|
+
verifier,
|
36
|
+
{
|
37
|
+
:redirect_uri => callback_url,
|
38
|
+
action: 'requesttoken',
|
39
|
+
client_id: options.client_id,
|
40
|
+
client_secret: options.client_secret,
|
41
|
+
code: verifier,
|
42
|
+
grant_type: 'authorization_code'
|
43
|
+
}.merge(token_params.to_hash(:symbolize_keys => true)),
|
44
|
+
deep_symbolize(options.auth_token_params))
|
45
|
+
end
|
46
|
+
|
30
47
|
alias :oauth2_access_token :access_token
|
31
48
|
|
32
49
|
def access_token
|
@@ -0,0 +1,224 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OmniAuth
|
4
|
+
module WithingsOauth2
|
5
|
+
class AccessToken # rubocop:disable Metrics/ClassLength
|
6
|
+
TOKEN_KEYS_STR = %w[access_token id_token token accessToken idToken].freeze
|
7
|
+
TOKEN_KEYS_SYM = %i[access_token id_token token accessToken idToken].freeze
|
8
|
+
TOKEN_KEY_LOOKUP = TOKEN_KEYS_STR + TOKEN_KEYS_SYM
|
9
|
+
|
10
|
+
attr_reader :client, :token, :expires_in, :expires_at, :expires_latency, :params
|
11
|
+
attr_accessor :options, :refresh_token, :response
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# Initializes an AccessToken from a Hash
|
15
|
+
#
|
16
|
+
# @param [Client] client the OAuth2::Client instance
|
17
|
+
# @param [Hash] hash a hash of AccessToken property values
|
18
|
+
# @option hash [String, Symbol] 'access_token', 'id_token', 'token', :access_token, :id_token, or :token the access token
|
19
|
+
# @return [AccessToken] the initialized AccessToken
|
20
|
+
def from_hash(client, hash)
|
21
|
+
fresh = hash["body"].dup
|
22
|
+
supported_keys = TOKEN_KEY_LOOKUP & fresh.keys
|
23
|
+
key = supported_keys[0]
|
24
|
+
extra_tokens_warning(supported_keys, key)
|
25
|
+
token = fresh.delete(key)
|
26
|
+
new(client, token, fresh)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Initializes an AccessToken from a key/value application/x-www-form-urlencoded string
|
30
|
+
#
|
31
|
+
# @param [Client] client the OAuth2::Client instance
|
32
|
+
# @param [String] kvform the application/x-www-form-urlencoded string
|
33
|
+
# @return [AccessToken] the initialized AccessToken
|
34
|
+
def from_kvform(client, kvform)
|
35
|
+
from_hash(client, Rack::Utils.parse_query(kvform))
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Having too many is sus, and may lead to bugs. Having none is fine (e.g. refresh flow doesn't need a token).
|
41
|
+
def extra_tokens_warning(supported_keys, key)
|
42
|
+
# return if OAuth2.config.silence_extra_tokens_warning
|
43
|
+
return if supported_keys.length <= 1
|
44
|
+
|
45
|
+
warn("OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key (#{supported_keys}); using #{key.inspect}.")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Initialize an AccessToken
|
50
|
+
#
|
51
|
+
# @param [Client] client the OAuth2::Client instance
|
52
|
+
# @param [String] token the Access Token value (optional, may not be used in refresh flows)
|
53
|
+
# @param [Hash] opts the options to create the Access Token with
|
54
|
+
# @option opts [String] :refresh_token (nil) the refresh_token value
|
55
|
+
# @option opts [FixNum, String] :expires_in (nil) the number of seconds in which the AccessToken will expire
|
56
|
+
# @option opts [FixNum, String] :expires_at (nil) the epoch time in seconds in which AccessToken will expire
|
57
|
+
# @option opts [FixNum, String] :expires_latency (nil) the number of seconds by which AccessToken validity will be reduced to offset latency, @version 2.0+
|
58
|
+
# @option opts [Symbol] :mode (:header) the transmission mode of the Access Token parameter value
|
59
|
+
# one of :header, :body or :query
|
60
|
+
# @option opts [String] :header_format ('Bearer %s') the string format to use for the Authorization header
|
61
|
+
# @option opts [String] :param_name ('access_token') the parameter name to use for transmission of the
|
62
|
+
# Access Token value in :body or :query transmission mode
|
63
|
+
def initialize(client, token, opts = {})
|
64
|
+
@client = client
|
65
|
+
@token = token.to_s
|
66
|
+
|
67
|
+
opts = opts.dup
|
68
|
+
%i[refresh_token expires_in expires_at expires_latency].each do |arg|
|
69
|
+
instance_variable_set("@#{arg}", opts.delete(arg) || opts.delete(arg.to_s))
|
70
|
+
end
|
71
|
+
no_tokens = (@token.nil? || @token.empty?) && (@refresh_token.nil? || @refresh_token.empty?)
|
72
|
+
if no_tokens
|
73
|
+
if @client.options[:raise_errors]
|
74
|
+
error = Error.new(opts)
|
75
|
+
raise(error)
|
76
|
+
else
|
77
|
+
warn('OAuth2::AccessToken has no token')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
# @option opts [Fixnum, String] :expires is deprecated
|
81
|
+
@expires_in ||= opts.delete('expires')
|
82
|
+
@expires_in &&= @expires_in.to_i
|
83
|
+
@expires_at &&= convert_expires_at(@expires_at)
|
84
|
+
@expires_latency &&= @expires_latency.to_i
|
85
|
+
@expires_at ||= Time.now.to_i + @expires_in if @expires_in
|
86
|
+
@expires_at -= @expires_latency if @expires_latency
|
87
|
+
@options = {mode: opts.delete(:mode) || :header,
|
88
|
+
header_format: opts.delete(:header_format) || 'Bearer %s',
|
89
|
+
param_name: opts.delete(:param_name) || 'access_token'}
|
90
|
+
@params = opts
|
91
|
+
end
|
92
|
+
|
93
|
+
# Indexer to additional params present in token response
|
94
|
+
#
|
95
|
+
# @param [String] key entry key to Hash
|
96
|
+
def [](key)
|
97
|
+
@params[key]
|
98
|
+
end
|
99
|
+
|
100
|
+
# Whether or not the token expires
|
101
|
+
#
|
102
|
+
# @return [Boolean]
|
103
|
+
def expires?
|
104
|
+
!!@expires_at
|
105
|
+
end
|
106
|
+
|
107
|
+
# Whether or not the token is expired
|
108
|
+
#
|
109
|
+
# @return [Boolean]
|
110
|
+
def expired?
|
111
|
+
expires? && (expires_at <= Time.now.to_i)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Refreshes the current Access Token
|
115
|
+
#
|
116
|
+
# @return [AccessToken] a new AccessToken
|
117
|
+
# @note options should be carried over to the new AccessToken
|
118
|
+
def refresh(params = {}, access_token_opts = {})
|
119
|
+
raise('A refresh_token is not available') unless refresh_token
|
120
|
+
|
121
|
+
params[:grant_type] = 'refresh_token'
|
122
|
+
params[:refresh_token] = refresh_token
|
123
|
+
new_token = @client.get_token(params, access_token_opts)
|
124
|
+
new_token.options = options
|
125
|
+
if new_token.refresh_token
|
126
|
+
# Keep it, if there is one
|
127
|
+
else
|
128
|
+
new_token.refresh_token = refresh_token
|
129
|
+
end
|
130
|
+
new_token
|
131
|
+
end
|
132
|
+
# A compatibility alias
|
133
|
+
# @note does not modify the receiver, so bang is not the default method
|
134
|
+
alias refresh! refresh
|
135
|
+
|
136
|
+
# Convert AccessToken to a hash which can be used to rebuild itself with AccessToken.from_hash
|
137
|
+
#
|
138
|
+
# @return [Hash] a hash of AccessToken property values
|
139
|
+
def to_hash
|
140
|
+
params.merge(access_token: token, refresh_token: refresh_token, expires_at: expires_at)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Make a request with the Access Token
|
144
|
+
#
|
145
|
+
# @param [Symbol] verb the HTTP request method
|
146
|
+
# @param [String] path the HTTP URL path of the request
|
147
|
+
# @param [Hash] opts the options to make the request with
|
148
|
+
# @see Client#request
|
149
|
+
def request(verb, path, opts = {}, &block)
|
150
|
+
configure_authentication!(opts)
|
151
|
+
@client.request(verb, path, opts, &block)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Make a GET request with the Access Token
|
155
|
+
#
|
156
|
+
# @see AccessToken#request
|
157
|
+
def get(path, opts = {}, &block)
|
158
|
+
request(:get, path, opts, &block)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Make a POST request with the Access Token
|
162
|
+
#
|
163
|
+
# @see AccessToken#request
|
164
|
+
def post(path, opts = {}, &block)
|
165
|
+
request(:post, path, opts, &block)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Make a PUT request with the Access Token
|
169
|
+
#
|
170
|
+
# @see AccessToken#request
|
171
|
+
def put(path, opts = {}, &block)
|
172
|
+
request(:put, path, opts, &block)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Make a PATCH request with the Access Token
|
176
|
+
#
|
177
|
+
# @see AccessToken#request
|
178
|
+
def patch(path, opts = {}, &block)
|
179
|
+
request(:patch, path, opts, &block)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Make a DELETE request with the Access Token
|
183
|
+
#
|
184
|
+
# @see AccessToken#request
|
185
|
+
def delete(path, opts = {}, &block)
|
186
|
+
request(:delete, path, opts, &block)
|
187
|
+
end
|
188
|
+
|
189
|
+
# Get the headers hash (includes Authorization token)
|
190
|
+
def headers
|
191
|
+
{'Authorization' => options[:header_format] % token}
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def configure_authentication!(opts)
|
197
|
+
case options[:mode]
|
198
|
+
when :header
|
199
|
+
opts[:headers] ||= {}
|
200
|
+
opts[:headers].merge!(headers)
|
201
|
+
when :query
|
202
|
+
opts[:params] ||= {}
|
203
|
+
opts[:params][options[:param_name]] = token
|
204
|
+
when :body
|
205
|
+
opts[:body] ||= {}
|
206
|
+
if opts[:body].is_a?(Hash)
|
207
|
+
opts[:body][options[:param_name]] = token
|
208
|
+
else
|
209
|
+
opts[:body] += "&#{options[:param_name]}=#{token}"
|
210
|
+
end
|
211
|
+
# @todo support for multi-part (file uploads)
|
212
|
+
else
|
213
|
+
raise("invalid :mode option of #{options[:mode]}")
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def convert_expires_at(expires_at)
|
218
|
+
Time.iso8601(expires_at.to_s).to_i
|
219
|
+
rescue ArgumentError
|
220
|
+
expires_at.to_i
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
19
|
gem.require_paths = ["lib"]
|
20
20
|
|
21
|
-
gem.add_runtime_dependency 'omniauth-oauth2', '~> 1.
|
21
|
+
gem.add_runtime_dependency 'omniauth-oauth2', '~> 1.8'
|
22
22
|
|
23
23
|
gem.add_development_dependency 'bundler', '>= 1.16', '< 3.0'
|
24
24
|
gem.add_development_dependency 'rake', '~> 12.3'
|
@@ -18,7 +18,7 @@ describe OmniAuth::Strategies::Withings do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'has correct `token_url`' do
|
21
|
-
expect(subject.client.options[:token_url]).to eq('https://
|
21
|
+
expect(subject.client.options[:token_url]).to eq('https://wbsapi.withings.net/v2/oauth2')
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniauth-withings-oauth2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Shelley
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: omniauth-oauth2
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.8'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.8'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- lib/omniauth-withings-oauth2.rb
|
106
106
|
- lib/omniauth-withings-oauth2/version.rb
|
107
107
|
- lib/omniauth/strategies/withings.rb
|
108
|
+
- lib/omniauth/withings_oauth2/access_token.rb
|
108
109
|
- omniauth-withings-oauth2.gemspec
|
109
110
|
- spec/omniauth/strategies/withings_spec.rb
|
110
111
|
- spec/spec_helper.rb
|
@@ -112,7 +113,7 @@ homepage: https://github.com/bartimaeus/omniauth-withings-oauth2
|
|
112
113
|
licenses:
|
113
114
|
- MIT
|
114
115
|
metadata: {}
|
115
|
-
post_install_message:
|
116
|
+
post_install_message:
|
116
117
|
rdoc_options: []
|
117
118
|
require_paths:
|
118
119
|
- lib
|
@@ -127,9 +128,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
127
128
|
- !ruby/object:Gem::Version
|
128
129
|
version: '0'
|
129
130
|
requirements: []
|
130
|
-
|
131
|
-
|
132
|
-
signing_key:
|
131
|
+
rubygems_version: 3.3.7
|
132
|
+
signing_key:
|
133
133
|
specification_version: 4
|
134
134
|
summary: Withings OAuth2 strategy for OmniAuth.
|
135
135
|
test_files:
|