googleauth 0.9.0 → 0.17.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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +11 -0
  3. data/CHANGELOG.md +113 -21
  4. data/README.md +13 -15
  5. data/SECURITY.md +7 -0
  6. data/lib/googleauth/application_default.rb +9 -9
  7. data/lib/googleauth/compute_engine.rb +55 -30
  8. data/lib/googleauth/credentials.rb +253 -64
  9. data/lib/googleauth/credentials_loader.rb +15 -16
  10. data/lib/googleauth/iam.rb +1 -1
  11. data/{spec/googleauth/stores/store_examples.rb → lib/googleauth/id_tokens/errors.rb} +36 -23
  12. data/lib/googleauth/id_tokens/key_sources.rb +396 -0
  13. data/lib/googleauth/id_tokens/verifier.rb +142 -0
  14. data/lib/googleauth/id_tokens.rb +233 -0
  15. data/lib/googleauth/json_key_reader.rb +6 -2
  16. data/lib/googleauth/scope_util.rb +1 -1
  17. data/lib/googleauth/service_account.rb +61 -36
  18. data/lib/googleauth/signet.rb +9 -7
  19. data/lib/googleauth/stores/file_token_store.rb +1 -0
  20. data/lib/googleauth/stores/redis_token_store.rb +1 -0
  21. data/lib/googleauth/user_authorizer.rb +8 -3
  22. data/lib/googleauth/user_refresh.rb +1 -1
  23. data/lib/googleauth/version.rb +1 -1
  24. data/lib/googleauth/web_user_authorizer.rb +5 -8
  25. data/lib/googleauth.rb +1 -0
  26. metadata +33 -76
  27. data/.github/CONTRIBUTING.md +0 -74
  28. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -36
  29. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -21
  30. data/.github/ISSUE_TEMPLATE/support_request.md +0 -7
  31. data/.gitignore +0 -36
  32. data/.kokoro/build.bat +0 -16
  33. data/.kokoro/build.sh +0 -4
  34. data/.kokoro/continuous/common.cfg +0 -24
  35. data/.kokoro/continuous/linux.cfg +0 -15
  36. data/.kokoro/continuous/osx.cfg +0 -3
  37. data/.kokoro/continuous/windows.cfg +0 -19
  38. data/.kokoro/osx.sh +0 -4
  39. data/.kokoro/presubmit/common.cfg +0 -24
  40. data/.kokoro/presubmit/linux.cfg +0 -14
  41. data/.kokoro/presubmit/osx.cfg +0 -3
  42. data/.kokoro/presubmit/windows.cfg +0 -19
  43. data/.kokoro/release.cfg +0 -53
  44. data/.kokoro/trampoline.bat +0 -10
  45. data/.kokoro/trampoline.sh +0 -4
  46. data/.rspec +0 -2
  47. data/.rubocop.yml +0 -42
  48. data/Gemfile +0 -25
  49. data/Rakefile +0 -89
  50. data/googleauth.gemspec +0 -35
  51. data/spec/googleauth/apply_auth_examples.rb +0 -148
  52. data/spec/googleauth/client_id_spec.rb +0 -160
  53. data/spec/googleauth/compute_engine_spec.rb +0 -122
  54. data/spec/googleauth/credentials_spec.rb +0 -459
  55. data/spec/googleauth/get_application_default_spec.rb +0 -286
  56. data/spec/googleauth/iam_spec.rb +0 -80
  57. data/spec/googleauth/scope_util_spec.rb +0 -77
  58. data/spec/googleauth/service_account_spec.rb +0 -482
  59. data/spec/googleauth/signet_spec.rb +0 -134
  60. data/spec/googleauth/stores/file_token_store_spec.rb +0 -57
  61. data/spec/googleauth/stores/redis_token_store_spec.rb +0 -50
  62. data/spec/googleauth/user_authorizer_spec.rb +0 -323
  63. data/spec/googleauth/user_refresh_spec.rb +0 -359
  64. data/spec/googleauth/web_user_authorizer_spec.rb +0 -172
  65. data/spec/spec_helper.rb +0 -92
  66. /data/{COPYING → LICENSE} +0 -0
@@ -0,0 +1,233 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2020 Google LLC
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are
7
+ # met:
8
+ #
9
+ # * Redistributions of source code must retain the above copyright
10
+ # notice, this list of conditions and the following disclaimer.
11
+ # * Redistributions in binary form must reproduce the above
12
+ # copyright notice, this list of conditions and the following disclaimer
13
+ # in the documentation and/or other materials provided with the
14
+ # distribution.
15
+ # * Neither the name of Google Inc. nor the names of its
16
+ # contributors may be used to endorse or promote products derived from
17
+ # this software without specific prior written permission.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+
31
+ require "googleauth/id_tokens/errors"
32
+ require "googleauth/id_tokens/key_sources"
33
+ require "googleauth/id_tokens/verifier"
34
+
35
+ module Google
36
+ module Auth
37
+ ##
38
+ # ## Verifying Google ID tokens
39
+ #
40
+ # This module verifies ID tokens issued by Google. This can be used to
41
+ # authenticate signed-in users using OpenID Connect. See
42
+ # https://developers.google.com/identity/sign-in/web/backend-auth for more
43
+ # information.
44
+ #
45
+ # ### Basic usage
46
+ #
47
+ # To verify an ID token issued by Google accounts:
48
+ #
49
+ # payload = Google::Auth::IDTokens.verify_oidc the_token,
50
+ # aud: "my-app-client-id"
51
+ #
52
+ # If verification succeeds, you will receive the token's payload as a hash.
53
+ # If verification fails, an exception (normally a subclass of
54
+ # {Google::Auth::IDTokens::VerificationError}) will be raised.
55
+ #
56
+ # To verify an ID token issued by the Google identity-aware proxy (IAP):
57
+ #
58
+ # payload = Google::Auth::IDTokens.verify_iap the_token,
59
+ # aud: "my-app-client-id"
60
+ #
61
+ # These methods will automatically download and cache the Google public
62
+ # keys necessary to verify these tokens. They will also automatically
63
+ # verify the issuer (`iss`) field for their respective types of ID tokens.
64
+ #
65
+ # ### Advanced usage
66
+ #
67
+ # If you want to provide your own public keys, either by pointing at a
68
+ # custom URI or by providing the key data directly, use the Verifier class
69
+ # and pass in a key source.
70
+ #
71
+ # To point to a custom URI that returns a JWK set:
72
+ #
73
+ # source = Google::Auth::IDTokens::JwkHttpKeySource.new "https://example.com/jwk"
74
+ # verifier = Google::Auth::IDTokens::Verifier.new key_source: source
75
+ # payload = verifier.verify the_token, aud: "my-app-client-id"
76
+ #
77
+ # To provide key data directly:
78
+ #
79
+ # jwk_data = {
80
+ # keys: [
81
+ # {
82
+ # alg: "ES256",
83
+ # crv: "P-256",
84
+ # kid: "LYyP2g",
85
+ # kty: "EC",
86
+ # use: "sig",
87
+ # x: "SlXFFkJ3JxMsXyXNrqzE3ozl_0913PmNbccLLWfeQFU",
88
+ # y: "GLSahrZfBErmMUcHP0MGaeVnJdBwquhrhQ8eP05NfCI"
89
+ # }
90
+ # ]
91
+ # }
92
+ # source = Google::Auth::IDTokens::StaticKeySource.from_jwk_set jwk_data
93
+ # verifier = Google::Auth::IDTokens::Verifier key_source: source
94
+ # payload = verifier.verify the_token, aud: "my-app-client-id"
95
+ #
96
+ module IDTokens
97
+ ##
98
+ # A list of issuers expected for Google OIDC-issued tokens.
99
+ #
100
+ # @return [Array<String>]
101
+ #
102
+ OIDC_ISSUERS = ["accounts.google.com", "https://accounts.google.com"].freeze
103
+
104
+ ##
105
+ # A list of issuers expected for Google IAP-issued tokens.
106
+ #
107
+ # @return [Array<String>]
108
+ #
109
+ IAP_ISSUERS = ["https://cloud.google.com/iap"].freeze
110
+
111
+ ##
112
+ # The URL for Google OAuth2 V3 public certs
113
+ #
114
+ # @return [String]
115
+ #
116
+ OAUTH2_V3_CERTS_URL = "https://www.googleapis.com/oauth2/v3/certs"
117
+
118
+ ##
119
+ # The URL for Google IAP public keys
120
+ #
121
+ # @return [String]
122
+ #
123
+ IAP_JWK_URL = "https://www.gstatic.com/iap/verify/public_key-jwk"
124
+
125
+ class << self
126
+ ##
127
+ # The key source providing public keys that can be used to verify
128
+ # ID tokens issued by Google OIDC.
129
+ #
130
+ # @return [Google::Auth::IDTokens::JwkHttpKeySource]
131
+ #
132
+ def oidc_key_source
133
+ @oidc_key_source ||= JwkHttpKeySource.new OAUTH2_V3_CERTS_URL
134
+ end
135
+
136
+ ##
137
+ # The key source providing public keys that can be used to verify
138
+ # ID tokens issued by Google IAP.
139
+ #
140
+ # @return [Google::Auth::IDTokens::JwkHttpKeySource]
141
+ #
142
+ def iap_key_source
143
+ @iap_key_source ||= JwkHttpKeySource.new IAP_JWK_URL
144
+ end
145
+
146
+ ##
147
+ # Reset all convenience key sources. Used for testing.
148
+ # @private
149
+ #
150
+ def forget_sources!
151
+ @oidc_key_source = @iap_key_source = nil
152
+ self
153
+ end
154
+
155
+ ##
156
+ # A convenience method that verifies a token allegedly issued by Google
157
+ # OIDC.
158
+ #
159
+ # @param token [String] The ID token to verify
160
+ # @param aud [String,Array<String>,nil] The expected audience. At least
161
+ # one `aud` field in the token must match at least one of the
162
+ # provided audiences, or the verification will fail with
163
+ # {Google::Auth::IDToken::AudienceMismatchError}. If `nil` (the
164
+ # default), no audience checking is performed.
165
+ # @param azp [String,Array<String>,nil] The expected authorized party
166
+ # (azp). At least one `azp` field in the token must match at least
167
+ # one of the provided values, or the verification will fail with
168
+ # {Google::Auth::IDToken::AuthorizedPartyMismatchError}. If `nil`
169
+ # (the default), no azp checking is performed.
170
+ # @param aud [String,Array<String>,nil] The expected audience. At least
171
+ # one `iss` field in the token must match at least one of the
172
+ # provided issuers, or the verification will fail with
173
+ # {Google::Auth::IDToken::IssuerMismatchError}. If `nil`, no issuer
174
+ # checking is performed. Default is to check against {OIDC_ISSUERS}.
175
+ #
176
+ # @return [Hash] The decoded token payload.
177
+ # @raise [KeySourceError] if the key source failed to obtain public keys
178
+ # @raise [VerificationError] if the token verification failed.
179
+ # Additional data may be available in the error subclass and message.
180
+ #
181
+ def verify_oidc token,
182
+ aud: nil,
183
+ azp: nil,
184
+ iss: OIDC_ISSUERS
185
+
186
+ verifier = Verifier.new key_source: oidc_key_source,
187
+ aud: aud,
188
+ azp: azp,
189
+ iss: iss
190
+ verifier.verify token
191
+ end
192
+
193
+ ##
194
+ # A convenience method that verifies a token allegedly issued by Google
195
+ # IAP.
196
+ #
197
+ # @param token [String] The ID token to verify
198
+ # @param aud [String,Array<String>,nil] The expected audience. At least
199
+ # one `aud` field in the token must match at least one of the
200
+ # provided audiences, or the verification will fail with
201
+ # {Google::Auth::IDToken::AudienceMismatchError}. If `nil` (the
202
+ # default), no audience checking is performed.
203
+ # @param azp [String,Array<String>,nil] The expected authorized party
204
+ # (azp). At least one `azp` field in the token must match at least
205
+ # one of the provided values, or the verification will fail with
206
+ # {Google::Auth::IDToken::AuthorizedPartyMismatchError}. If `nil`
207
+ # (the default), no azp checking is performed.
208
+ # @param aud [String,Array<String>,nil] The expected audience. At least
209
+ # one `iss` field in the token must match at least one of the
210
+ # provided issuers, or the verification will fail with
211
+ # {Google::Auth::IDToken::IssuerMismatchError}. If `nil`, no issuer
212
+ # checking is performed. Default is to check against {IAP_ISSUERS}.
213
+ #
214
+ # @return [Hash] The decoded token payload.
215
+ # @raise [KeySourceError] if the key source failed to obtain public keys
216
+ # @raise [VerificationError] if the token verification failed.
217
+ # Additional data may be available in the error subclass and message.
218
+ #
219
+ def verify_iap token,
220
+ aud: nil,
221
+ azp: nil,
222
+ iss: IAP_ISSUERS
223
+
224
+ verifier = Verifier.new key_source: iap_key_source,
225
+ aud: aud,
226
+ azp: azp,
227
+ iss: iss
228
+ verifier.verify token
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
@@ -38,8 +38,12 @@ module Google
38
38
  json_key = MultiJson.load json_key_io.read
39
39
  raise "missing client_email" unless json_key.key? "client_email"
40
40
  raise "missing private_key" unless json_key.key? "private_key"
41
- project_id = json_key["project_id"]
42
- [json_key["private_key"], json_key["client_email"], project_id]
41
+ [
42
+ json_key["private_key"],
43
+ json_key["client_email"],
44
+ json_key["project_id"],
45
+ json_key["quota_project_id"]
46
+ ]
43
47
  end
44
48
  end
45
49
  end
@@ -51,7 +51,7 @@ module Google
51
51
  when Array
52
52
  scope
53
53
  when String
54
- scope.split " "
54
+ scope.split
55
55
  else
56
56
  raise "Invalid scope value. Must be string or array"
57
57
  end
@@ -45,34 +45,47 @@ module Google
45
45
  # from credentials from a json key file downloaded from the developer
46
46
  # console (via 'Generate new Json Key').
47
47
  #
48
- # cf [Application Default Credentials](http://goo.gl/mkAHpZ)
48
+ # cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
49
49
  class ServiceAccountCredentials < Signet::OAuth2::Client
50
50
  TOKEN_CRED_URI = "https://www.googleapis.com/oauth2/v4/token".freeze
51
51
  extend CredentialsLoader
52
52
  extend JsonKeyReader
53
53
  attr_reader :project_id
54
+ attr_reader :quota_project_id
55
+
56
+ def enable_self_signed_jwt?
57
+ @enable_self_signed_jwt
58
+ end
54
59
 
55
60
  # Creates a ServiceAccountCredentials.
56
61
  #
57
62
  # @param json_key_io [IO] an IO from which the JSON key can be read
58
63
  # @param scope [string|array|nil] the scope(s) to access
59
64
  def self.make_creds options = {}
60
- json_key_io, scope = options.values_at :json_key_io, :scope
65
+ json_key_io, scope, enable_self_signed_jwt, target_audience, audience, token_credential_uri =
66
+ options.values_at :json_key_io, :scope, :enable_self_signed_jwt, :target_audience,
67
+ :audience, :token_credential_uri
68
+ raise ArgumentError, "Cannot specify both scope and target_audience" if scope && target_audience
69
+
61
70
  if json_key_io
62
- private_key, client_email, project_id = read_json_key json_key_io
71
+ private_key, client_email, project_id, quota_project_id = read_json_key json_key_io
63
72
  else
64
73
  private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
65
74
  client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
66
75
  project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
76
+ quota_project_id = nil
67
77
  end
68
78
  project_id ||= CredentialsLoader.load_gcloud_project_id
69
79
 
70
- new(token_credential_uri: TOKEN_CRED_URI,
71
- audience: TOKEN_CRED_URI,
72
- scope: scope,
73
- issuer: client_email,
74
- signing_key: OpenSSL::PKey::RSA.new(private_key),
75
- project_id: project_id)
80
+ new(token_credential_uri: token_credential_uri || TOKEN_CRED_URI,
81
+ audience: audience || TOKEN_CRED_URI,
82
+ scope: scope,
83
+ enable_self_signed_jwt: enable_self_signed_jwt,
84
+ target_audience: target_audience,
85
+ issuer: client_email,
86
+ signing_key: OpenSSL::PKey::RSA.new(private_key),
87
+ project_id: project_id,
88
+ quota_project_id: quota_project_id)
76
89
  .configure_connection(options)
77
90
  end
78
91
 
@@ -87,30 +100,36 @@ module Google
87
100
 
88
101
  def initialize options = {}
89
102
  @project_id = options[:project_id]
103
+ @quota_project_id = options[:quota_project_id]
104
+ @enable_self_signed_jwt = options[:enable_self_signed_jwt] ? true : false
90
105
  super options
91
106
  end
92
107
 
93
- # Extends the base class.
94
- #
95
- # If scope(s) is not set, it creates a transient
96
- # ServiceAccountJwtHeaderCredentials instance and uses that to
97
- # authenticate instead.
108
+ # Extends the base class to use a transient
109
+ # ServiceAccountJwtHeaderCredentials for certain cases.
98
110
  def apply! a_hash, opts = {}
99
- # Use the base implementation if scopes are set
100
- unless scope.nil?
111
+ # Use a self-singed JWT if there's no information that can be used to
112
+ # obtain an OAuth token, OR if there are scopes but also an assertion
113
+ # that they are default scopes that shouldn't be used to fetch a token.
114
+ if target_audience.nil? && (scope.nil? || enable_self_signed_jwt?)
115
+ apply_self_signed_jwt! a_hash
116
+ else
101
117
  super
102
- return
103
118
  end
119
+ end
120
+
121
+ private
104
122
 
123
+ def apply_self_signed_jwt! a_hash
105
124
  # Use the ServiceAccountJwtHeaderCredentials using the same cred values
106
- # if no scopes are set.
107
125
  cred_json = {
108
- private_key: @signing_key.to_s,
109
- client_email: @issuer
126
+ private_key: @signing_key.to_s,
127
+ client_email: @issuer,
128
+ project_id: @project_id,
129
+ quota_project_id: @quota_project_id
110
130
  }
111
- alt_clz = ServiceAccountJwtHeaderCredentials
112
131
  key_io = StringIO.new MultiJson.dump(cred_json)
113
- alt = alt_clz.make_creds json_key_io: key_io
132
+ alt = ServiceAccountJwtHeaderCredentials.make_creds json_key_io: key_io, scope: scope
114
133
  alt.apply! a_hash
115
134
  end
116
135
  end
@@ -123,7 +142,7 @@ module Google
123
142
  # console (via 'Generate new Json Key'). It is not part of any OAuth2
124
143
  # flow, rather it creates a JWT and sends that as a credential.
125
144
  #
126
- # cf [Application Default Credentials](http://goo.gl/mkAHpZ)
145
+ # cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
127
146
  class ServiceAccountJwtHeaderCredentials
128
147
  JWT_AUD_URI_KEY = :jwt_aud_uri
129
148
  AUTH_METADATA_KEY = Signet::OAuth2::AUTH_METADATA_KEY
@@ -133,16 +152,15 @@ module Google
133
152
  extend CredentialsLoader
134
153
  extend JsonKeyReader
135
154
  attr_reader :project_id
155
+ attr_reader :quota_project_id
136
156
 
137
- # make_creds proxies the construction of a credentials instance
157
+ # Create a ServiceAccountJwtHeaderCredentials.
138
158
  #
139
- # make_creds is used by the methods in CredentialsLoader.
140
- #
141
- # By default, it calls #new with 2 args, the second one being an
142
- # optional scope. Here's the constructor only has one param, so
143
- # we modify make_creds to reflect this.
144
- def self.make_creds *args
145
- new json_key_io: args[0][:json_key_io]
159
+ # @param json_key_io [IO] an IO from which the JSON key can be read
160
+ # @param scope [string|array|nil] the scope(s) to access
161
+ def self.make_creds options = {}
162
+ json_key_io, scope = options.values_at :json_key_io, :scope
163
+ new json_key_io: json_key_io, scope: scope
146
164
  end
147
165
 
148
166
  # Initializes a ServiceAccountJwtHeaderCredentials.
@@ -151,15 +169,17 @@ module Google
151
169
  def initialize options = {}
152
170
  json_key_io = options[:json_key_io]
153
171
  if json_key_io
154
- @private_key, @issuer, @project_id =
172
+ @private_key, @issuer, @project_id, @quota_project_id =
155
173
  self.class.read_json_key json_key_io
156
174
  else
157
175
  @private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
158
176
  @issuer = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
159
177
  @project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
178
+ @quota_project_id = nil
160
179
  end
161
180
  @project_id ||= CredentialsLoader.load_gcloud_project_id
162
181
  @signing_key = OpenSSL::PKey::RSA.new @private_key
182
+ @scope = options[:scope]
163
183
  end
164
184
 
165
185
  # Construct a jwt token if the JWT_AUD_URI key is present in the input
@@ -168,7 +188,7 @@ module Google
168
188
  # The jwt token is used as the value of a 'Bearer '.
169
189
  def apply! a_hash, opts = {}
170
190
  jwt_aud_uri = a_hash.delete JWT_AUD_URI_KEY
171
- return a_hash if jwt_aud_uri.nil?
191
+ return a_hash if jwt_aud_uri.nil? && @scope.nil?
172
192
  jwt_token = new_jwt_token jwt_aud_uri, opts
173
193
  a_hash[AUTH_METADATA_KEY] = "Bearer #{jwt_token}"
174
194
  a_hash
@@ -184,22 +204,27 @@ module Google
184
204
  # Returns a reference to the #apply method, suitable for passing as
185
205
  # a closure
186
206
  def updater_proc
187
- lambda(&method(:apply))
207
+ proc { |a_hash, opts = {}| apply a_hash, opts }
188
208
  end
189
209
 
190
210
  protected
191
211
 
192
212
  # Creates a jwt uri token.
193
- def new_jwt_token jwt_aud_uri, options = {}
213
+ def new_jwt_token jwt_aud_uri = nil, options = {}
194
214
  now = Time.new
195
215
  skew = options[:skew] || 60
196
216
  assertion = {
197
217
  "iss" => @issuer,
198
218
  "sub" => @issuer,
199
- "aud" => jwt_aud_uri,
200
219
  "exp" => (now + EXPIRY).to_i,
201
220
  "iat" => (now - skew).to_i
202
221
  }
222
+
223
+ jwt_aud_uri = nil if @scope
224
+
225
+ assertion["scope"] = Array(@scope).join " " if @scope
226
+ assertion["aud"] = jwt_aud_uri if jwt_aud_uri
227
+
203
228
  JWT.encode assertion, @signing_key, SIGNING_ALGORITHM
204
229
  end
205
230
  end
@@ -48,8 +48,9 @@ module Signet
48
48
  def apply! a_hash, opts = {}
49
49
  # fetch the access token there is currently not one, or if the client
50
50
  # has expired
51
- fetch_access_token! opts if access_token.nil? || expires_within?(60)
52
- a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}"
51
+ token_type = target_audience ? :id_token : :access_token
52
+ fetch_access_token! opts if send(token_type).nil? || expires_within?(60)
53
+ a_hash[AUTH_METADATA_KEY] = "Bearer #{send token_type}"
53
54
  end
54
55
 
55
56
  # Returns a clone of a_hash updated with the authentication token
@@ -62,11 +63,11 @@ module Signet
62
63
  # Returns a reference to the #apply method, suitable for passing as
63
64
  # a closure
64
65
  def updater_proc
65
- lambda(&method(:apply))
66
+ proc { |a_hash, opts = {}| apply a_hash, opts }
66
67
  end
67
68
 
68
69
  def on_refresh &block
69
- @refresh_listeners ||= []
70
+ @refresh_listeners = [] unless defined? @refresh_listeners
70
71
  @refresh_listeners << block
71
72
  end
72
73
 
@@ -84,15 +85,16 @@ module Signet
84
85
  end
85
86
 
86
87
  def notify_refresh_listeners
87
- listeners = @refresh_listeners || []
88
+ listeners = defined?(@refresh_listeners) ? @refresh_listeners : []
88
89
  listeners.each do |block|
89
90
  block.call self
90
91
  end
91
92
  end
92
93
 
93
94
  def build_default_connection
94
- return nil unless defined?(@connection_info)
95
- if @connection_info.respond_to? :call
95
+ if !defined?(@connection_info)
96
+ nil
97
+ elsif @connection_info.respond_to? :call
96
98
  @connection_info.call
97
99
  else
98
100
  @connection_info
@@ -40,6 +40,7 @@ module Google
40
40
  # @param [String, File] file
41
41
  # Path to storage file
42
42
  def initialize options = {}
43
+ super()
43
44
  path = options[:file]
44
45
  @store = YAML::Store.new path
45
46
  end
@@ -49,6 +49,7 @@ module Google
49
49
  # the options passed through. You may include any other keys accepted
50
50
  # by `Redis.new`
51
51
  def initialize options = {}
52
+ super()
52
53
  redis = options.delete :redis
53
54
  prefix = options.delete :prefix
54
55
  @redis = case redis
@@ -129,8 +129,8 @@ module Google
129
129
  data = MultiJson.load saved_token
130
130
 
131
131
  if data.fetch("client_id", @client_id.id) != @client_id.id
132
- raise sprintf(MISMATCHED_CLIENT_ID_ERROR,
133
- data["client_id"], @client_id.id)
132
+ raise format(MISMATCHED_CLIENT_ID_ERROR,
133
+ data["client_id"], @client_id.id)
134
134
  end
135
135
 
136
136
  credentials = UserRefreshCredentials.new(
@@ -271,10 +271,15 @@ module Google
271
271
  # @return [String]
272
272
  # Redirect URI
273
273
  def redirect_uri_for base_url
274
- return @callback_uri unless URI(@callback_uri).scheme.nil?
274
+ return @callback_uri if uri_is_postmessage?(@callback_uri) || !URI(@callback_uri).scheme.nil?
275
275
  raise format(MISSING_ABSOLUTE_URL_ERROR, @callback_uri) if base_url.nil? || URI(base_url).scheme.nil?
276
276
  URI.join(base_url, @callback_uri).to_s
277
277
  end
278
+
279
+ # Check if URI is Google's postmessage flow (not a valid redirect_uri by spec, but allowed)
280
+ def uri_is_postmessage? uri
281
+ uri.to_s.casecmp("postmessage").zero?
282
+ end
278
283
  end
279
284
  end
280
285
  end
@@ -44,7 +44,7 @@ module Google
44
44
  # 'gcloud auth login' saves a file with these contents in well known
45
45
  # location
46
46
  #
47
- # cf [Application Default Credentials](http://goo.gl/mkAHpZ)
47
+ # cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
48
48
  class UserRefreshCredentials < Signet::OAuth2::Client
49
49
  TOKEN_CRED_URI = "https://oauth2.googleapis.com/token".freeze
50
50
  AUTHORIZATION_URI = "https://accounts.google.com/o/oauth2/auth".freeze
@@ -31,6 +31,6 @@ module Google
31
31
  # Module Auth provides classes that provide Google-specific authorization
32
32
  # used to access Google APIs.
33
33
  module Auth
34
- VERSION = "0.9.0".freeze
34
+ VERSION = "0.17.1".freeze
35
35
  end
36
36
  end
@@ -58,12 +58,9 @@ module Google
58
58
  # end
59
59
  #
60
60
  # Instead of implementing the callback directly, applications are
61
- # encouraged to use {Google::Auth::Web::AuthCallbackApp} instead.
61
+ # encouraged to use {Google::Auth::WebUserAuthorizer::CallbackApp} instead.
62
62
  #
63
- # For rails apps, see {Google::Auth::ControllerHelpers}
64
- #
65
- # @see {Google::Auth::AuthCallbackApp}
66
- # @see {Google::Auth::ControllerHelpers}
63
+ # @see CallbackApp
67
64
  # @note Requires sessions are enabled
68
65
  class WebUserAuthorizer < Google::Auth::UserAuthorizer
69
66
  STATE_PARAM = "state".freeze
@@ -171,7 +168,7 @@ module Google
171
168
  options[:state] = MultiJson.dump(state.merge(
172
169
  SESSION_ID_KEY => request.session[XSRF_KEY],
173
170
  CURRENT_URI_KEY => redirect_to
174
- ))
171
+ ))
175
172
  options[:base_url] = request.url
176
173
  super options
177
174
  end
@@ -192,7 +189,7 @@ module Google
192
189
  # May raise an error if an authorization code is present in the session
193
190
  # and exchange of the code fails
194
191
  def get_credentials user_id, request = nil, scope = nil
195
- if request && request.session.key?(CALLBACK_STATE_KEY)
192
+ if request&.session&.key? CALLBACK_STATE_KEY
196
193
  # Note - in theory, no need to check required scope as this is
197
194
  # expected to be called immediately after a return from authorization
198
195
  state_json = request.session.delete CALLBACK_STATE_KEY
@@ -261,7 +258,7 @@ module Google
261
258
  # Google::Auth::WebUserAuthorizer::CallbackApp.call(env)
262
259
  # end
263
260
  #
264
- # @see {Google::Auth::WebUserAuthorizer}
261
+ # @see Google::Auth::WebUserAuthorizer
265
262
  class CallbackApp
266
263
  LOCATION_HEADER = "Location".freeze
267
264
  REDIR_STATUS = 302
data/lib/googleauth.rb CHANGED
@@ -31,5 +31,6 @@ require "googleauth/application_default"
31
31
  require "googleauth/client_id"
32
32
  require "googleauth/credentials"
33
33
  require "googleauth/default_credentials"
34
+ require "googleauth/id_tokens"
34
35
  require "googleauth/user_authorizer"
35
36
  require "googleauth/web_user_authorizer"