signet 0.5.0 → 0.5.1
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 +7 -0
- data/CHANGELOG.md +13 -0
- data/Rakefile +1 -1
- data/lib/compat/base64.rb +7 -0
- data/lib/signet/oauth_1.rb +38 -28
- data/lib/signet/oauth_1/client.rb +85 -22
- data/lib/signet/oauth_1/signature_methods/plaintext.rb +21 -0
- data/lib/signet/oauth_1/signature_methods/rsa_sha1.rb +21 -0
- data/lib/signet/oauth_2/client.rb +126 -61
- data/lib/signet/version.rb +4 -4
- data/spec/signet/oauth_1/client_spec.rb +36 -0
- data/spec/signet/oauth_1/signature_methods/plaintext_spec.rb +66 -0
- data/spec/signet/oauth_1/signature_methods/rsa_sha1_spec.rb +132 -0
- data/spec/signet/oauth_2/client_spec.rb +134 -13
- metadata +68 -46
- data/Gemfile.lock +0 -45
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a90553898c900b623be7bcd66492052606823e88
|
4
|
+
data.tar.gz: 79d812b9a1bf1c00a548b48a5434dd8a3f40cb39
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b41031268b8c1e10cea6cc132edcb85954c398927ecd7c364c401fab64930758c47a87d4ffb4b1c196f6c6e9dedd3a2cf893d939910440649f560c39555b0546
|
7
|
+
data.tar.gz: be0549ccf4f39e5d7d0a67b209884c798bfcec5bda4618a27e6e96579cec21ff54d76f08c64cf606925cf9c04015c72c678f3066b3cfa75daf60648ae8bb6c29
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# 0.5.1
|
2
|
+
|
3
|
+
* Allow Hash objects to be used to initialize authorization URI
|
4
|
+
* Added PLAINTEXT and RSA-SHA1 signature methods to OAuth 1 support
|
5
|
+
* Added client object serialization
|
6
|
+
* The `approval_prompt` option no longer defaults to `:force`
|
7
|
+
* The `approval_prompt` and `prompt` are now mutually exclusive.
|
8
|
+
|
9
|
+
# 0.5.0
|
10
|
+
|
11
|
+
* Switched to faraday 0.9.0
|
12
|
+
* Added `expires_at` option
|
13
|
+
|
1
14
|
# 0.4.5
|
2
15
|
|
3
16
|
* Minor documentation fixes
|
data/Rakefile
CHANGED
@@ -26,7 +26,7 @@ PKG_FILES = FileList[
|
|
26
26
|
"lib/**/*", "spec/**/*", "vendor/**/*",
|
27
27
|
"tasks/**/*", "website/**/*",
|
28
28
|
"[A-Z]*", "Rakefile"
|
29
|
-
].exclude(/database\.yml/).exclude(/[_\.]git$/)
|
29
|
+
].exclude(/database\.yml/).exclude(/[_\.]git$/).exclude(/Gemfile\.lock/)
|
30
30
|
|
31
31
|
RCOV_ENABLED = !!(RUBY_PLATFORM != 'java' && RUBY_VERSION =~ /^1\.8/)
|
32
32
|
if RCOV_ENABLED
|
data/lib/signet/oauth_1.rb
CHANGED
@@ -69,27 +69,27 @@ module Signet #:nodoc:
|
|
69
69
|
#
|
70
70
|
# @return [String] The credential key value.
|
71
71
|
def self.extract_credential_key_option(credential_type, options)
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
if options[
|
77
|
-
credential_key = options[
|
78
|
-
elsif options[
|
72
|
+
# Normalize key to String to allow indifferent access.
|
73
|
+
options = options.inject({}) { |accu, (k, v)| accu[k.to_s] = v; accu }
|
74
|
+
credential_key = "#{credential_type}_credential_key"
|
75
|
+
credential = "#{credential_type}_credential"
|
76
|
+
if options[credential_key]
|
77
|
+
credential_key = options[credential_key]
|
78
|
+
elsif options[credential]
|
79
79
|
require 'signet/oauth_1/credential'
|
80
|
-
if !options[
|
80
|
+
if !options[credential].respond_to?(:key)
|
81
81
|
raise TypeError,
|
82
82
|
"Expected Signet::OAuth1::Credential, " +
|
83
|
-
"got #{options[
|
83
|
+
"got #{options[credential].class}."
|
84
84
|
end
|
85
|
-
credential_key = options[
|
86
|
-
elsif options[
|
85
|
+
credential_key = options[credential].key
|
86
|
+
elsif options["client"]
|
87
87
|
require 'signet/oauth_1/client'
|
88
|
-
if !options[
|
88
|
+
if !options["client"].kind_of?(::Signet::OAuth1::Client)
|
89
89
|
raise TypeError,
|
90
|
-
"Expected Signet::OAuth1::Client, got #{options[
|
90
|
+
"Expected Signet::OAuth1::Client, got #{options["client"].class}."
|
91
91
|
end
|
92
|
-
credential_key = options[
|
92
|
+
credential_key = options["client"].send(credential_key)
|
93
93
|
else
|
94
94
|
credential_key = nil
|
95
95
|
end
|
@@ -111,27 +111,27 @@ module Signet #:nodoc:
|
|
111
111
|
#
|
112
112
|
# @return [String] The credential secret value.
|
113
113
|
def self.extract_credential_secret_option(credential_type, options)
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
if options[
|
119
|
-
credential_secret = options[
|
120
|
-
elsif options[
|
114
|
+
# Normalize key to String to allow indifferent access.
|
115
|
+
options = options.inject({}) { |accu, (k, v)| accu[k.to_s] = v; accu }
|
116
|
+
credential_secret = "#{credential_type}_credential_secret"
|
117
|
+
credential = "#{credential_type}_credential"
|
118
|
+
if options[credential_secret]
|
119
|
+
credential_secret = options[credential_secret]
|
120
|
+
elsif options[credential]
|
121
121
|
require 'signet/oauth_1/credential'
|
122
|
-
if !options[
|
122
|
+
if !options[credential].respond_to?(:secret)
|
123
123
|
raise TypeError,
|
124
124
|
"Expected Signet::OAuth1::Credential, " +
|
125
|
-
"got #{options[
|
125
|
+
"got #{options[credential].class}."
|
126
126
|
end
|
127
|
-
credential_secret = options[
|
128
|
-
elsif options[
|
127
|
+
credential_secret = options[credential].secret
|
128
|
+
elsif options["client"]
|
129
129
|
require 'signet/oauth_1/client'
|
130
|
-
if !options[
|
130
|
+
if !options["client"].kind_of?(::Signet::OAuth1::Client)
|
131
131
|
raise TypeError,
|
132
|
-
"Expected Signet::OAuth1::Client, got #{options[
|
132
|
+
"Expected Signet::OAuth1::Client, got #{options["client"].class}."
|
133
133
|
end
|
134
|
-
credential_secret = options[
|
134
|
+
credential_secret = options["client"].send(credential_secret)
|
135
135
|
else
|
136
136
|
credential_secret = nil
|
137
137
|
end
|
@@ -295,6 +295,16 @@ module Signet #:nodoc:
|
|
295
295
|
return Signet::OAuth1::HMACSHA1.generate_signature(
|
296
296
|
base_string, client_credential_secret, token_credential_secret
|
297
297
|
)
|
298
|
+
when 'RSA-SHA1'
|
299
|
+
require 'signet/oauth_1/signature_methods/rsa_sha1'
|
300
|
+
return Signet::OAuth1::RSASHA1.generate_signature(
|
301
|
+
base_string, client_credential_secret, token_credential_secret
|
302
|
+
)
|
303
|
+
when 'PLAINTEXT'
|
304
|
+
require 'signet/oauth_1/signature_methods/plaintext'
|
305
|
+
return Signet::OAuth1::PLAINTEXT.generate_signature(
|
306
|
+
base_string, client_credential_secret, token_credential_secret
|
307
|
+
)
|
298
308
|
else
|
299
309
|
raise NotImplementedError,
|
300
310
|
"Unsupported signature method: #{signature_method}"
|
@@ -54,25 +54,62 @@ module Signet
|
|
54
54
|
# :client_credential_secret => 'anonymous'
|
55
55
|
# )
|
56
56
|
def initialize(options={})
|
57
|
-
self.
|
58
|
-
|
59
|
-
|
57
|
+
self.update!(options)
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Updates an OAuth 1.0 client.
|
62
|
+
#
|
63
|
+
# @param [Hash] options
|
64
|
+
# The configuration parameters for the client.
|
65
|
+
# - <code>:temporary_credential_uri</code> -
|
66
|
+
# The OAuth temporary credentials URI.
|
67
|
+
# - <code>:authorization_uri</code> -
|
68
|
+
# The OAuth authorization URI.
|
69
|
+
# - <code>:token_credential_uri</code> -
|
70
|
+
# The OAuth token credentials URI.
|
71
|
+
# - <code>:client_credential_key</code> -
|
72
|
+
# The OAuth client credential key.
|
73
|
+
# - <code>:client_credential_secret</code> -
|
74
|
+
# The OAuth client credential secret.
|
75
|
+
# - <code>:callback</code> - The OAuth callback. Defaults to 'oob'.
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# client.update!(
|
79
|
+
# :temporary_credential_uri =>
|
80
|
+
# 'https://www.google.com/accounts/OAuthGetRequestToken',
|
81
|
+
# :authorization_uri =>
|
82
|
+
# 'https://www.google.com/accounts/OAuthAuthorizeToken',
|
83
|
+
# :token_credential_uri =>
|
84
|
+
# 'https://www.google.com/accounts/OAuthGetAccessToken',
|
85
|
+
# :client_credential_key => 'anonymous',
|
86
|
+
# :client_credential_secret => 'anonymous'
|
87
|
+
# )
|
88
|
+
#
|
89
|
+
# @see Signet::OAuth1::Client#initialize
|
90
|
+
def update!(options={})
|
91
|
+
# Normalize key to String to allow indifferent access.
|
92
|
+
options = options.inject({}) { |accu, (k, v)| accu[k.to_s] = v; accu }
|
93
|
+
self.temporary_credential_uri = options["temporary_credential_uri"]
|
94
|
+
self.authorization_uri = options["authorization_uri"]
|
95
|
+
self.token_credential_uri = options["token_credential_uri"]
|
60
96
|
# Technically... this would allow you to pass in a :client key...
|
61
97
|
# But that would be weird. Don't do that.
|
62
98
|
self.client_credential_key =
|
63
|
-
Signet::OAuth1.extract_credential_key_option(
|
99
|
+
Signet::OAuth1.extract_credential_key_option("client", options)
|
64
100
|
self.client_credential_secret =
|
65
|
-
Signet::OAuth1.extract_credential_secret_option(
|
101
|
+
Signet::OAuth1.extract_credential_secret_option("client", options)
|
66
102
|
self.temporary_credential_key =
|
67
|
-
Signet::OAuth1.extract_credential_key_option(
|
103
|
+
Signet::OAuth1.extract_credential_key_option("temporary", options)
|
68
104
|
self.temporary_credential_secret =
|
69
|
-
Signet::OAuth1.extract_credential_secret_option(
|
105
|
+
Signet::OAuth1.extract_credential_secret_option("temporary", options)
|
70
106
|
self.token_credential_key =
|
71
|
-
Signet::OAuth1.extract_credential_key_option(
|
107
|
+
Signet::OAuth1.extract_credential_key_option("token", options)
|
72
108
|
self.token_credential_secret =
|
73
|
-
Signet::OAuth1.extract_credential_secret_option(
|
74
|
-
self.callback = options[
|
75
|
-
self.two_legged = options[
|
109
|
+
Signet::OAuth1.extract_credential_secret_option("token", options)
|
110
|
+
self.callback = options["callback"]
|
111
|
+
self.two_legged = options["two_legged"] || false
|
112
|
+
return self
|
76
113
|
end
|
77
114
|
|
78
115
|
##
|
@@ -127,8 +164,10 @@ module Signet
|
|
127
164
|
# The authorization URI.
|
128
165
|
def authorization_uri=(new_authorization_uri)
|
129
166
|
if new_authorization_uri != nil
|
130
|
-
new_authorization_uri =
|
131
|
-
|
167
|
+
new_authorization_uri = Addressable::URI.send(
|
168
|
+
new_authorization_uri.kind_of?(Hash) ? :new : :parse,
|
169
|
+
new_authorization_uri
|
170
|
+
)
|
132
171
|
@authorization_uri = new_authorization_uri
|
133
172
|
else
|
134
173
|
@authorization_uri = nil
|
@@ -147,12 +186,14 @@ module Signet
|
|
147
186
|
##
|
148
187
|
# Sets the token credential URI for this client.
|
149
188
|
#
|
150
|
-
# @param [Addressable::URI, String, #to_str] new_token_credential_uri
|
189
|
+
# @param [Addressable::URI, Hash, String, #to_str] new_token_credential_uri
|
151
190
|
# The token credential URI.
|
152
191
|
def token_credential_uri=(new_token_credential_uri)
|
153
192
|
if new_token_credential_uri != nil
|
154
|
-
new_token_credential_uri =
|
155
|
-
|
193
|
+
new_token_credential_uri = Addressable::URI.send(
|
194
|
+
new_token_credential_uri.kind_of?(Hash) ? :new : :parse,
|
195
|
+
new_token_credential_uri
|
196
|
+
)
|
156
197
|
@token_credential_uri = new_token_credential_uri
|
157
198
|
else
|
158
199
|
@token_credential_uri = nil
|
@@ -509,6 +550,28 @@ module Signet
|
|
509
550
|
end
|
510
551
|
end
|
511
552
|
|
553
|
+
##
|
554
|
+
# Serialize the client object to JSON.
|
555
|
+
#
|
556
|
+
# @note A serialized client contains sensitive information. Persist or transmit with care.
|
557
|
+
#
|
558
|
+
# @return [String] A serialized JSON representation of the client.
|
559
|
+
def to_json
|
560
|
+
return MultiJson.dump({
|
561
|
+
'temporary_credential_uri' => self.temporary_credential_uri,
|
562
|
+
'authorization_uri' => self.authorization_uri,
|
563
|
+
'token_credential_uri' => self.token_credential_uri,
|
564
|
+
'callback' => self.callback,
|
565
|
+
'two_legged' => self.two_legged,
|
566
|
+
'client_credential_key' => self.client_credential_key,
|
567
|
+
'client_credential_secret' => self.client_credential_secret,
|
568
|
+
'temporary_credential_key' => self.temporary_credential_key,
|
569
|
+
'temporary_credential_secret' => self.temporary_credential_secret,
|
570
|
+
'token_credential_key' => self.token_credential_key,
|
571
|
+
'token_credential_secret' => self.token_credential_secret
|
572
|
+
})
|
573
|
+
end
|
574
|
+
|
512
575
|
##
|
513
576
|
# Generates a request for temporary credentials.
|
514
577
|
#
|
@@ -859,7 +922,7 @@ module Signet
|
|
859
922
|
:realm => nil,
|
860
923
|
:connection => Faraday.default_connection
|
861
924
|
}.merge(options)
|
862
|
-
|
925
|
+
|
863
926
|
if options[:request].kind_of?(Faraday::Request)
|
864
927
|
request = options[:request]
|
865
928
|
else
|
@@ -902,16 +965,16 @@ module Signet
|
|
902
965
|
req.body = body
|
903
966
|
end
|
904
967
|
end
|
905
|
-
|
968
|
+
|
906
969
|
parameters = ::Signet::OAuth1.unsigned_resource_parameters(
|
907
970
|
:client_credential_key => self.client_credential_key,
|
908
971
|
:token_credential_key => self.token_credential_key,
|
909
972
|
:signature_method => options[:signature_method],
|
910
973
|
:two_legged => self.two_legged
|
911
974
|
)
|
912
|
-
|
975
|
+
|
913
976
|
env = request.to_env(options[:connection])
|
914
|
-
|
977
|
+
|
915
978
|
content_type = request['Content-Type'].to_s
|
916
979
|
content_type = content_type.split(';', 2).first if content_type.index(';')
|
917
980
|
if request.method == :post && content_type == 'application/x-www-form-urlencoded'
|
@@ -923,7 +986,7 @@ module Signet
|
|
923
986
|
post_parameters = Addressable::URI.form_unencode(env[:body])
|
924
987
|
parameters = parameters.concat(post_parameters)
|
925
988
|
end
|
926
|
-
|
989
|
+
|
927
990
|
# No need to attach URI query parameters, the .sign_parameters
|
928
991
|
# method takes care of that automatically.
|
929
992
|
signature = ::Signet::OAuth1.sign_parameters(
|
@@ -933,7 +996,7 @@ module Signet
|
|
933
996
|
self.client_credential_secret,
|
934
997
|
self.token_credential_secret
|
935
998
|
)
|
936
|
-
|
999
|
+
|
937
1000
|
parameters << ['oauth_signature', signature]
|
938
1001
|
request['Authorization'] = ::Signet::OAuth1.generate_authorization_header(
|
939
1002
|
parameters, options[:realm])
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'signet'
|
2
|
+
|
3
|
+
module Signet #:nodoc:
|
4
|
+
module OAuth1
|
5
|
+
module PLAINTEXT
|
6
|
+
def self.generate_signature(
|
7
|
+
base_string, client_credential_secret, token_credential_secret)
|
8
|
+
# Both the client secret and token secret must be escaped
|
9
|
+
client_credential_secret =
|
10
|
+
Signet::OAuth1.encode(client_credential_secret)
|
11
|
+
token_credential_secret =
|
12
|
+
Signet::OAuth1.encode(token_credential_secret)
|
13
|
+
# The key for the signature is just the client secret and token
|
14
|
+
# secret joined by the '&' character. If the token secret is omitted,
|
15
|
+
# the '&' must still be present.
|
16
|
+
key = [client_credential_secret, token_credential_secret].join("&")
|
17
|
+
return Signet::OAuth1.encode(key).strip
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
require 'base64'
|
3
|
+
require 'openssl'
|
4
|
+
require 'signet'
|
5
|
+
require 'compat/base64'
|
6
|
+
|
7
|
+
module Signet #:nodoc:
|
8
|
+
module OAuth1
|
9
|
+
module RSASHA1
|
10
|
+
def self.generate_signature(
|
11
|
+
base_string, client_credential_secret, token_credential_secret)
|
12
|
+
|
13
|
+
private_key = OpenSSL::PKey::RSA.new(client_credential_secret)
|
14
|
+
signature = private_key.sign(OpenSSL::Digest::SHA1.new, base_string)
|
15
|
+
#using strict_encode64 because the encode64 method adds newline characters after ever 60 chars
|
16
|
+
return Base64.strict_encode64(signature).strip
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -140,7 +140,7 @@ module Signet
|
|
140
140
|
# - <code>:id_token</code> -
|
141
141
|
# The current ID token for this client.
|
142
142
|
# - <code>:extension_parameters</code> -
|
143
|
-
# When using an extension grant type, this the set of parameters used
|
143
|
+
# When using an extension grant type, this is the set of parameters used
|
144
144
|
# by that extension.
|
145
145
|
#
|
146
146
|
# @example
|
@@ -155,22 +155,24 @@ module Signet
|
|
155
155
|
def update!(options={})
|
156
156
|
# Normalize key to String to allow indifferent access.
|
157
157
|
options = options.inject({}) { |accu, (k, v)| accu[k.to_s] = v; accu }
|
158
|
-
self.authorization_uri = options["authorization_uri"]
|
159
|
-
self.token_credential_uri = options["token_credential_uri"]
|
160
|
-
self.client_id = options["client_id"]
|
161
|
-
self.client_secret = options["client_secret"]
|
162
|
-
self.scope = options["scope"]
|
163
|
-
self.state = options["state"]
|
164
|
-
self.code = options["code"]
|
165
|
-
self.redirect_uri = options["redirect_uri"]
|
166
|
-
self.username = options["username"]
|
167
|
-
self.password = options["password"]
|
168
|
-
self.issuer = options["issuer"]
|
169
|
-
self.person = options["person"]
|
158
|
+
self.authorization_uri = options["authorization_uri"] if options.has_key?("authorization_uri")
|
159
|
+
self.token_credential_uri = options["token_credential_uri"] if options.has_key?("token_credential_uri")
|
160
|
+
self.client_id = options["client_id"] if options.has_key?("client_id")
|
161
|
+
self.client_secret = options["client_secret"] if options.has_key?("client_secret")
|
162
|
+
self.scope = options["scope"] if options.has_key?("scope")
|
163
|
+
self.state = options["state"] if options.has_key?("state")
|
164
|
+
self.code = options["code"] if options.has_key?("code")
|
165
|
+
self.redirect_uri = options["redirect_uri"] if options.has_key?("redirect_uri")
|
166
|
+
self.username = options["username"] if options.has_key?("username")
|
167
|
+
self.password = options["password"] if options.has_key?("password")
|
168
|
+
self.issuer = options["issuer"] if options.has_key?("issuer")
|
169
|
+
self.person = options["person"] if options.has_key?("person")
|
170
|
+
self.sub = options["sub"] if options.has_key?("sub")
|
170
171
|
self.expiry = options["expiry"] || 60
|
171
|
-
self.audience = options["audience"]
|
172
|
-
self.signing_key = options["signing_key"]
|
172
|
+
self.audience = options["audience"] if options.has_key?("audience")
|
173
|
+
self.signing_key = options["signing_key"] if options.has_key?("signing_key")
|
173
174
|
self.extension_parameters = options["extension_parameters"] || {}
|
175
|
+
self.additional_parameters = options["additional_parameters"] || {}
|
174
176
|
self.update_token!(options)
|
175
177
|
return self
|
176
178
|
end
|
@@ -207,25 +209,16 @@ module Signet
|
|
207
209
|
# Normalize key to String to allow indifferent access.
|
208
210
|
options = options.inject({}) { |accu, (k, v)| accu[k.to_s] = v; accu }
|
209
211
|
|
210
|
-
self.
|
211
|
-
self.
|
212
|
-
self.expires_at = options["expires_at"] if options["expires_at"]
|
212
|
+
self.expires_in = options["expires_in"] if options.has_key?("expires_in")
|
213
|
+
self.expires_at = options["expires_at"] if options.has_key?("expires_at")
|
213
214
|
|
214
|
-
# The refresh token may not be returned in a token response.
|
215
|
-
# In which case, the old one should continue to be used.
|
216
|
-
if options["refresh_token"]
|
217
|
-
self.refresh_token = options["refresh_token"]
|
218
|
-
end
|
219
|
-
# The ID token may not be returned in a token response.
|
220
|
-
# In which case, the old one should continue to be used.
|
221
|
-
if options["id_token"]
|
222
|
-
self.id_token = options["id_token"]
|
223
|
-
end
|
224
215
|
# By default, the token is issued at `Time.now` when `expires_in` is
|
225
216
|
# set, but this can be used to supply a more precise time.
|
226
|
-
|
227
|
-
|
228
|
-
|
217
|
+
self.issued_at = options["issued_at"] if options.has_key?("issued_at")
|
218
|
+
|
219
|
+
self.access_token = options["access_token"] if options.has_key?("access_token")
|
220
|
+
self.refresh_token = options["refresh_token"] if options.has_key?("refresh_token")
|
221
|
+
self.id_token = options["id_token"] if options.has_key?("id_token")
|
229
222
|
|
230
223
|
return self
|
231
224
|
end
|
@@ -244,12 +237,11 @@ module Signet
|
|
244
237
|
unless options[:access_type]
|
245
238
|
options[:access_type] = :offline
|
246
239
|
end
|
247
|
-
unless options[:approval_prompt]
|
248
|
-
# This default will likely change in the future.
|
249
|
-
options[:approval_prompt] = :force
|
250
|
-
end
|
251
240
|
options[:client_id] ||= self.client_id
|
252
241
|
options[:redirect_uri] ||= self.redirect_uri
|
242
|
+
if options[:prompt] && options[:approval_prompt]
|
243
|
+
raise ArgumentError, "prompt and approval_prompt are mutually exclusive parameters"
|
244
|
+
end
|
253
245
|
if !options[:client_id]
|
254
246
|
raise ArgumentError, "Missing required client identifier."
|
255
247
|
end
|
@@ -260,6 +252,8 @@ module Signet
|
|
260
252
|
options[:scope] = self.scope.join(' ')
|
261
253
|
end
|
262
254
|
options[:state] = self.state unless options[:state]
|
255
|
+
options.merge!(self.additional_parameters.merge(options[:additional_parameters] || {}))
|
256
|
+
options.delete(:additional_parameters)
|
263
257
|
uri = Addressable::URI.parse(
|
264
258
|
::Signet::OAuth2.generate_authorization_uri(
|
265
259
|
@authorization_uri, options
|
@@ -275,12 +269,14 @@ module Signet
|
|
275
269
|
##
|
276
270
|
# Sets the authorization URI for this client.
|
277
271
|
#
|
278
|
-
# @param [Addressable::URI, String, #to_str] new_authorization_uri
|
272
|
+
# @param [Addressable::URI, Hash, String, #to_str] new_authorization_uri
|
279
273
|
# The authorization URI.
|
280
274
|
def authorization_uri=(new_authorization_uri)
|
281
275
|
if new_authorization_uri != nil
|
282
|
-
new_authorization_uri =
|
283
|
-
|
276
|
+
new_authorization_uri = Addressable::URI.send(
|
277
|
+
new_authorization_uri.kind_of?(Hash) ? :new : :parse,
|
278
|
+
new_authorization_uri
|
279
|
+
)
|
284
280
|
@authorization_uri = new_authorization_uri
|
285
281
|
else
|
286
282
|
@authorization_uri = nil
|
@@ -298,12 +294,14 @@ module Signet
|
|
298
294
|
##
|
299
295
|
# Sets the token credential URI for this client.
|
300
296
|
#
|
301
|
-
# @param [Addressable::URI, String, #to_str] new_token_credential_uri
|
297
|
+
# @param [Addressable::URI, Hash, String, #to_str] new_token_credential_uri
|
302
298
|
# The token credential URI.
|
303
299
|
def token_credential_uri=(new_token_credential_uri)
|
304
300
|
if new_token_credential_uri != nil
|
305
|
-
new_token_credential_uri =
|
306
|
-
|
301
|
+
new_token_credential_uri = Addressable::URI.send(
|
302
|
+
new_token_credential_uri.kind_of?(Hash) ? :new : :parse,
|
303
|
+
new_token_credential_uri
|
304
|
+
)
|
307
305
|
@token_credential_uri = new_token_credential_uri
|
308
306
|
else
|
309
307
|
@token_credential_uri = nil
|
@@ -430,8 +428,8 @@ module Signet
|
|
430
428
|
# The redirect URI.
|
431
429
|
def redirect_uri=(new_redirect_uri)
|
432
430
|
new_redirect_uri = Addressable::URI.parse(new_redirect_uri)
|
433
|
-
#TODO - Better solution to allow google postmessage flow. For now, make an exception to the spec.
|
434
|
-
if new_redirect_uri == nil|| new_redirect_uri.absolute? || uri_is_postmessage?(new_redirect_uri)
|
431
|
+
#TODO - Better solution to allow google postmessage flow. For now, make an exception to the spec.
|
432
|
+
if new_redirect_uri == nil|| new_redirect_uri.absolute? || uri_is_postmessage?(new_redirect_uri) || uri_is_oob?(new_redirect_uri)
|
435
433
|
@redirect_uri = new_redirect_uri
|
436
434
|
else
|
437
435
|
raise ArgumentError, "Redirect URI must be an absolute URI."
|
@@ -532,10 +530,16 @@ module Signet
|
|
532
530
|
def principal=(new_person)
|
533
531
|
@principal = new_person
|
534
532
|
end
|
535
|
-
|
533
|
+
|
536
534
|
alias_method :person, :principal
|
537
535
|
alias_method :person=, :principal=
|
538
|
-
|
536
|
+
|
537
|
+
##
|
538
|
+
# The target "sub" when issuing assertions.
|
539
|
+
# Used in some Admin SDK APIs.
|
540
|
+
#
|
541
|
+
attr_accessor :sub
|
542
|
+
|
539
543
|
##
|
540
544
|
# Returns the number of seconds assertions are valid for
|
541
545
|
# Used only by the assertion grant type.
|
@@ -554,8 +558,8 @@ module Signet
|
|
554
558
|
def expiry=(new_expiry)
|
555
559
|
@expiry = new_expiry
|
556
560
|
end
|
557
|
-
|
558
|
-
|
561
|
+
|
562
|
+
|
559
563
|
##
|
560
564
|
# Returns the signing key associated with this client.
|
561
565
|
# Used only by the assertion grant type.
|
@@ -574,14 +578,14 @@ module Signet
|
|
574
578
|
def signing_key=(new_key)
|
575
579
|
@signing_key = new_key
|
576
580
|
end
|
577
|
-
|
581
|
+
|
578
582
|
##
|
579
583
|
# Algorithm used for signing JWTs
|
580
584
|
# @return [String] Signing algorithm
|
581
585
|
def signing_algorithm
|
582
586
|
self.signing_key.is_a?(String) ? "HS256" : "RS256"
|
583
587
|
end
|
584
|
-
|
588
|
+
|
585
589
|
##
|
586
590
|
# Returns the set of extension parameters used by the client.
|
587
591
|
# Used only by extension access grant types.
|
@@ -606,6 +610,28 @@ module Signet
|
|
606
610
|
end
|
607
611
|
end
|
608
612
|
|
613
|
+
##
|
614
|
+
# Returns the set of additional (non standard) parameters to be used by the client.
|
615
|
+
#
|
616
|
+
# @return [Hash] The pass through parameters.
|
617
|
+
def additional_parameters
|
618
|
+
return @additional_parameters ||= {}
|
619
|
+
end
|
620
|
+
|
621
|
+
##
|
622
|
+
# Sets additional (non standard) parameters to be used by the client.
|
623
|
+
#
|
624
|
+
# @param [Hash] new_additional_parameters
|
625
|
+
# The parameters.
|
626
|
+
def additional_parameters=(new_additional_parameters)
|
627
|
+
if new_additional_parameters.respond_to?(:to_hash)
|
628
|
+
@additional_parameters = new_additional_parameters.to_hash
|
629
|
+
else
|
630
|
+
raise TypeError,
|
631
|
+
"Expected Hash, got #{new_additional_parameters.class}."
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
609
635
|
##
|
610
636
|
# Returns the refresh token associated with this client.
|
611
637
|
#
|
@@ -745,8 +771,8 @@ module Signet
|
|
745
771
|
def expired?
|
746
772
|
return self.expires_at != nil && Time.now >= self.expires_at
|
747
773
|
end
|
748
|
-
|
749
|
-
|
774
|
+
|
775
|
+
|
750
776
|
##
|
751
777
|
# Removes all credentials from the client.
|
752
778
|
def clear_credentials!
|
@@ -801,7 +827,7 @@ module Signet
|
|
801
827
|
end
|
802
828
|
|
803
829
|
def to_jwt(options={})
|
804
|
-
now = Time.new
|
830
|
+
now = Time.new
|
805
831
|
skew = options[:skew] || 60
|
806
832
|
assertion = {
|
807
833
|
"iss" => self.issuer,
|
@@ -811,9 +837,40 @@ module Signet
|
|
811
837
|
"iat" => (now - skew).to_i
|
812
838
|
}
|
813
839
|
assertion['prn'] = self.person unless self.person.nil?
|
840
|
+
assertion['sub'] = self.sub unless self.sub.nil?
|
814
841
|
JWT.encode(assertion, self.signing_key, self.signing_algorithm)
|
815
842
|
end
|
816
|
-
|
843
|
+
|
844
|
+
##
|
845
|
+
# Serialize the client object to JSON.
|
846
|
+
#
|
847
|
+
# @note A serialized client contains sensitive information. Persist or transmit with care.
|
848
|
+
#
|
849
|
+
# @return [String] A serialized JSON representation of the client.
|
850
|
+
def to_json
|
851
|
+
return MultiJson.dump({
|
852
|
+
'authorization_uri' => self.authorization_uri,
|
853
|
+
'token_credential_uri' => self.token_credential_uri,
|
854
|
+
'client_id' => self.client_id,
|
855
|
+
'client_secret' => self.client_secret,
|
856
|
+
'scope' => self.scope,
|
857
|
+
'state' => self.state,
|
858
|
+
'code' => self.code,
|
859
|
+
'redirect_uri' => self.redirect_uri,
|
860
|
+
'username' => self.username,
|
861
|
+
'password' => self.password,
|
862
|
+
'issuer' => self.issuer,
|
863
|
+
'audience' => self.audience,
|
864
|
+
'person' => self.person,
|
865
|
+
'expiry' => self.expiry,
|
866
|
+
'signing_key' => self.signing_key,
|
867
|
+
'refresh_token' => self.refresh_token,
|
868
|
+
'access_token' => self.access_token,
|
869
|
+
'id_token' => self.id_token,
|
870
|
+
'extension_parameters' => self.extension_parameters
|
871
|
+
})
|
872
|
+
end
|
873
|
+
|
817
874
|
##
|
818
875
|
# Generates a request for token credentials.
|
819
876
|
#
|
@@ -856,6 +913,7 @@ module Signet
|
|
856
913
|
['Cache-Control', 'no-store'],
|
857
914
|
['Content-Type', 'application/x-www-form-urlencoded']
|
858
915
|
]
|
916
|
+
parameters.merge!(self.additional_parameters.merge(options[:additional_parameters] || {}))
|
859
917
|
return options[:connection].build_request(
|
860
918
|
method.to_s.downcase.to_sym
|
861
919
|
) do |req|
|
@@ -907,12 +965,12 @@ module Signet
|
|
907
965
|
return token_hash
|
908
966
|
end
|
909
967
|
|
910
|
-
##
|
968
|
+
##
|
911
969
|
# Refresh the access token, if possible
|
912
|
-
def refresh!
|
913
|
-
self.fetch_access_token!
|
970
|
+
def refresh!(options={})
|
971
|
+
self.fetch_access_token!(options)
|
914
972
|
end
|
915
|
-
|
973
|
+
|
916
974
|
##
|
917
975
|
# Generates an authenticated request for protected resources.
|
918
976
|
#
|
@@ -973,7 +1031,7 @@ module Signet
|
|
973
1031
|
req.body = body
|
974
1032
|
end
|
975
1033
|
end
|
976
|
-
|
1034
|
+
|
977
1035
|
request['Authorization'] = ::Signet::OAuth2.generate_bearer_authorization_header(
|
978
1036
|
self.access_token,
|
979
1037
|
options[:realm] ? [['realm', options[:realm]]] : nil
|
@@ -1042,16 +1100,23 @@ module Signet
|
|
1042
1100
|
return response
|
1043
1101
|
end
|
1044
1102
|
end
|
1045
|
-
|
1103
|
+
|
1046
1104
|
private
|
1047
|
-
|
1105
|
+
|
1048
1106
|
##
|
1049
1107
|
# Check if URI is Google's postmessage flow (not a valid redirect_uri by spec, but allowed)
|
1050
1108
|
# @private
|
1051
1109
|
def uri_is_postmessage?(uri)
|
1052
1110
|
return uri.to_s.casecmp('postmessage') == 0
|
1053
1111
|
end
|
1054
|
-
|
1112
|
+
|
1113
|
+
##
|
1114
|
+
# Check if the URI is a out-of-band
|
1115
|
+
# @private
|
1116
|
+
def uri_is_oob?(uri)
|
1117
|
+
return uri.to_s == 'urn:ietf:wg:oauth:2.0:oob' || uri.to_s == 'oob'
|
1118
|
+
end
|
1119
|
+
|
1055
1120
|
end
|
1056
1121
|
end
|
1057
1122
|
end
|