signet 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|