omniauth-withings-oauth2 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: 919a285f818c60af0dddcb963265cfeace3c6179c098f321590812d445a28015
|
4
|
+
data.tar.gz: a1f63f0a1ad0c1a8fa33a3fe7a5b4b757cb607522fbfb1ffac72588d66ef5fa3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 117810510a52949a4d9203727d308e4306c74f181cc10a552a35aaeb83e9818237710ade2aa0a2821f81379bf5f977bc0aaeb0f7e1b2c9a6fe1950aa9927a8f1
|
7
|
+
data.tar.gz: cf0e9d103ae7bd31e6603cad397523b2bb5d8f8d5248f3a41c2b6d6f66f6d082580055b351ffd073edae52ba5075a63c0860346e55c641145a884405b1e40c4a
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# OmniAuth Withings OAuth2 Strategy
|
2
2
|
|
3
3
|
[](https://travis-ci.org/bartimaeus/omniauth-withings-oauth2)
|
4
|
+
[](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.1.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:
|