googleauth 0.1.0 → 0.16.2
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/.github/CODEOWNERS +7 -0
- data/.github/CONTRIBUTING.md +74 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +36 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
- data/.github/ISSUE_TEMPLATE/support_request.md +7 -0
- data/.github/renovate.json +6 -0
- data/.github/sync-repo-settings.yaml +18 -0
- data/.github/workflows/ci.yml +55 -0
- data/.github/workflows/release-please.yml +39 -0
- data/.gitignore +3 -0
- data/.kokoro/populate-secrets.sh +76 -0
- data/.kokoro/release.cfg +52 -0
- data/.kokoro/release.sh +18 -0
- data/.kokoro/trampoline_v2.sh +489 -0
- data/.repo-metadata.json +5 -0
- data/.rubocop.yml +17 -0
- data/.toys/.toys.rb +45 -0
- data/.toys/ci.rb +43 -0
- data/.toys/kokoro/.toys.rb +66 -0
- data/.toys/kokoro/publish-docs.rb +67 -0
- data/.toys/kokoro/publish-gem.rb +53 -0
- data/.toys/linkinator.rb +43 -0
- data/.trampolinerc +48 -0
- data/CHANGELOG.md +199 -0
- data/CODE_OF_CONDUCT.md +43 -0
- data/Gemfile +22 -1
- data/{COPYING → LICENSE} +0 -0
- data/README.md +140 -17
- data/googleauth.gemspec +28 -28
- data/integration/helper.rb +31 -0
- data/integration/id_tokens/key_source_test.rb +74 -0
- data/lib/googleauth.rb +7 -37
- data/lib/googleauth/application_default.rb +81 -0
- data/lib/googleauth/client_id.rb +104 -0
- data/lib/googleauth/compute_engine.rb +73 -26
- data/lib/googleauth/credentials.rb +561 -0
- data/lib/googleauth/credentials_loader.rb +207 -0
- data/lib/googleauth/default_credentials.rb +93 -0
- data/lib/googleauth/iam.rb +75 -0
- data/lib/googleauth/id_tokens.rb +233 -0
- data/lib/googleauth/id_tokens/errors.rb +71 -0
- data/lib/googleauth/id_tokens/key_sources.rb +396 -0
- data/lib/googleauth/id_tokens/verifier.rb +142 -0
- data/lib/googleauth/json_key_reader.rb +50 -0
- data/lib/googleauth/scope_util.rb +61 -0
- data/lib/googleauth/service_account.rb +177 -67
- data/lib/googleauth/signet.rb +69 -8
- data/lib/googleauth/stores/file_token_store.rb +65 -0
- data/lib/googleauth/stores/redis_token_store.rb +96 -0
- data/lib/googleauth/token_store.rb +69 -0
- data/lib/googleauth/user_authorizer.rb +285 -0
- data/lib/googleauth/user_refresh.rb +129 -0
- data/lib/googleauth/version.rb +1 -1
- data/lib/googleauth/web_user_authorizer.rb +295 -0
- data/spec/googleauth/apply_auth_examples.rb +96 -94
- data/spec/googleauth/client_id_spec.rb +160 -0
- data/spec/googleauth/compute_engine_spec.rb +125 -55
- data/spec/googleauth/credentials_spec.rb +600 -0
- data/spec/googleauth/get_application_default_spec.rb +232 -80
- data/spec/googleauth/iam_spec.rb +80 -0
- data/spec/googleauth/scope_util_spec.rb +77 -0
- data/spec/googleauth/service_account_spec.rb +422 -68
- data/spec/googleauth/signet_spec.rb +101 -25
- data/spec/googleauth/stores/file_token_store_spec.rb +57 -0
- data/spec/googleauth/stores/redis_token_store_spec.rb +50 -0
- data/spec/googleauth/stores/store_examples.rb +58 -0
- data/spec/googleauth/user_authorizer_spec.rb +343 -0
- data/spec/googleauth/user_refresh_spec.rb +359 -0
- data/spec/googleauth/web_user_authorizer_spec.rb +172 -0
- data/spec/spec_helper.rb +51 -10
- data/test/helper.rb +33 -0
- data/test/id_tokens/key_sources_test.rb +240 -0
- data/test/id_tokens/verifier_test.rb +269 -0
- metadata +114 -75
- data/.travis.yml +0 -18
- data/CONTRIBUTING.md +0 -32
- data/Rakefile +0 -15
@@ -0,0 +1,207 @@
|
|
1
|
+
# Copyright 2015, Google Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are
|
6
|
+
# met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright
|
9
|
+
# notice, this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above
|
11
|
+
# copyright notice, this list of conditions and the following disclaimer
|
12
|
+
# in the documentation and/or other materials provided with the
|
13
|
+
# distribution.
|
14
|
+
# * Neither the name of Google Inc. nor the names of its
|
15
|
+
# contributors may be used to endorse or promote products derived from
|
16
|
+
# this software without specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
19
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
20
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
21
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
22
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
23
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
24
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
26
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
27
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
require "memoist"
|
31
|
+
require "os"
|
32
|
+
require "rbconfig"
|
33
|
+
|
34
|
+
module Google
|
35
|
+
# Module Auth provides classes that provide Google-specific authorization
|
36
|
+
# used to access Google APIs.
|
37
|
+
module Auth
|
38
|
+
# CredentialsLoader contains the behaviour used to locate and find default
|
39
|
+
# credentials files on the file system.
|
40
|
+
module CredentialsLoader
|
41
|
+
extend Memoist
|
42
|
+
ENV_VAR = "GOOGLE_APPLICATION_CREDENTIALS".freeze
|
43
|
+
PRIVATE_KEY_VAR = "GOOGLE_PRIVATE_KEY".freeze
|
44
|
+
CLIENT_EMAIL_VAR = "GOOGLE_CLIENT_EMAIL".freeze
|
45
|
+
CLIENT_ID_VAR = "GOOGLE_CLIENT_ID".freeze
|
46
|
+
CLIENT_SECRET_VAR = "GOOGLE_CLIENT_SECRET".freeze
|
47
|
+
REFRESH_TOKEN_VAR = "GOOGLE_REFRESH_TOKEN".freeze
|
48
|
+
ACCOUNT_TYPE_VAR = "GOOGLE_ACCOUNT_TYPE".freeze
|
49
|
+
PROJECT_ID_VAR = "GOOGLE_PROJECT_ID".freeze
|
50
|
+
GCLOUD_POSIX_COMMAND = "gcloud".freeze
|
51
|
+
GCLOUD_WINDOWS_COMMAND = "gcloud.cmd".freeze
|
52
|
+
GCLOUD_CONFIG_COMMAND =
|
53
|
+
"config config-helper --format json --verbosity none".freeze
|
54
|
+
|
55
|
+
CREDENTIALS_FILE_NAME = "application_default_credentials.json".freeze
|
56
|
+
NOT_FOUND_ERROR =
|
57
|
+
"Unable to read the credential file specified by #{ENV_VAR}".freeze
|
58
|
+
WELL_KNOWN_PATH = "gcloud/#{CREDENTIALS_FILE_NAME}".freeze
|
59
|
+
WELL_KNOWN_ERROR = "Unable to read the default credential file".freeze
|
60
|
+
|
61
|
+
SYSTEM_DEFAULT_ERROR =
|
62
|
+
"Unable to read the system default credential file".freeze
|
63
|
+
|
64
|
+
CLOUD_SDK_CLIENT_ID = "764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.app"\
|
65
|
+
"s.googleusercontent.com".freeze
|
66
|
+
|
67
|
+
CLOUD_SDK_CREDENTIALS_WARNING = "Your application has authenticated using end user "\
|
68
|
+
"credentials from Google Cloud SDK. We recommend that most server applications use "\
|
69
|
+
"service accounts instead. If your application continues to use end user credentials "\
|
70
|
+
'from Cloud SDK, you might receive a "quota exceeded" or "API not enabled" error. For '\
|
71
|
+
"more information about service accounts, see "\
|
72
|
+
"https://cloud.google.com/docs/authentication/. To suppress this message, set the "\
|
73
|
+
"GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS environment variable.".freeze
|
74
|
+
|
75
|
+
# make_creds proxies the construction of a credentials instance
|
76
|
+
#
|
77
|
+
# By default, it calls #new on the current class, but this behaviour can
|
78
|
+
# be modified, allowing different instances to be created.
|
79
|
+
def make_creds *args
|
80
|
+
creds = new(*args)
|
81
|
+
creds = creds.configure_connection args[0] if creds.respond_to?(:configure_connection) && args.size == 1
|
82
|
+
creds
|
83
|
+
end
|
84
|
+
|
85
|
+
# Creates an instance from the path specified in an environment
|
86
|
+
# variable.
|
87
|
+
#
|
88
|
+
# @param scope [string|array|nil] the scope(s) to access
|
89
|
+
# @param options [Hash] Connection options. These may be used to configure
|
90
|
+
# how OAuth tokens are retrieved, by providing a suitable
|
91
|
+
# `Faraday::Connection`. For example, if a connection proxy must be
|
92
|
+
# used in the current network, you may provide a connection with
|
93
|
+
# with the needed proxy options.
|
94
|
+
# The following keys are recognized:
|
95
|
+
# * `:default_connection` The connection object to use.
|
96
|
+
# * `:connection_builder` A `Proc` that returns a connection.
|
97
|
+
def from_env scope = nil, options = {}
|
98
|
+
options = interpret_options scope, options
|
99
|
+
if ENV.key?(ENV_VAR) && !ENV[ENV_VAR].empty?
|
100
|
+
path = ENV[ENV_VAR]
|
101
|
+
raise "file #{path} does not exist" unless File.exist? path
|
102
|
+
File.open path do |f|
|
103
|
+
return make_creds options.merge(json_key_io: f)
|
104
|
+
end
|
105
|
+
elsif service_account_env_vars? || authorized_user_env_vars?
|
106
|
+
make_creds options
|
107
|
+
end
|
108
|
+
rescue StandardError => e
|
109
|
+
raise "#{NOT_FOUND_ERROR}: #{e}"
|
110
|
+
end
|
111
|
+
|
112
|
+
# Creates an instance from a well known path.
|
113
|
+
#
|
114
|
+
# @param scope [string|array|nil] the scope(s) to access
|
115
|
+
# @param options [Hash] Connection options. These may be used to configure
|
116
|
+
# how OAuth tokens are retrieved, by providing a suitable
|
117
|
+
# `Faraday::Connection`. For example, if a connection proxy must be
|
118
|
+
# used in the current network, you may provide a connection with
|
119
|
+
# with the needed proxy options.
|
120
|
+
# The following keys are recognized:
|
121
|
+
# * `:default_connection` The connection object to use.
|
122
|
+
# * `:connection_builder` A `Proc` that returns a connection.
|
123
|
+
def from_well_known_path scope = nil, options = {}
|
124
|
+
options = interpret_options scope, options
|
125
|
+
home_var = OS.windows? ? "APPDATA" : "HOME"
|
126
|
+
base = WELL_KNOWN_PATH
|
127
|
+
root = ENV[home_var].nil? ? "" : ENV[home_var]
|
128
|
+
base = File.join ".config", base unless OS.windows?
|
129
|
+
path = File.join root, base
|
130
|
+
return nil unless File.exist? path
|
131
|
+
File.open path do |f|
|
132
|
+
return make_creds options.merge(json_key_io: f)
|
133
|
+
end
|
134
|
+
rescue StandardError => e
|
135
|
+
raise "#{WELL_KNOWN_ERROR}: #{e}"
|
136
|
+
end
|
137
|
+
|
138
|
+
# Creates an instance from the system default path
|
139
|
+
#
|
140
|
+
# @param scope [string|array|nil] the scope(s) to access
|
141
|
+
# @param options [Hash] Connection options. These may be used to configure
|
142
|
+
# how OAuth tokens are retrieved, by providing a suitable
|
143
|
+
# `Faraday::Connection`. For example, if a connection proxy must be
|
144
|
+
# used in the current network, you may provide a connection with
|
145
|
+
# with the needed proxy options.
|
146
|
+
# The following keys are recognized:
|
147
|
+
# * `:default_connection` The connection object to use.
|
148
|
+
# * `:connection_builder` A `Proc` that returns a connection.
|
149
|
+
def from_system_default_path scope = nil, options = {}
|
150
|
+
options = interpret_options scope, options
|
151
|
+
if OS.windows?
|
152
|
+
return nil unless ENV["ProgramData"]
|
153
|
+
prefix = File.join ENV["ProgramData"], "Google/Auth"
|
154
|
+
else
|
155
|
+
prefix = "/etc/google/auth/"
|
156
|
+
end
|
157
|
+
path = File.join prefix, CREDENTIALS_FILE_NAME
|
158
|
+
return nil unless File.exist? path
|
159
|
+
File.open path do |f|
|
160
|
+
return make_creds options.merge(json_key_io: f)
|
161
|
+
end
|
162
|
+
rescue StandardError => e
|
163
|
+
raise "#{SYSTEM_DEFAULT_ERROR}: #{e}"
|
164
|
+
end
|
165
|
+
|
166
|
+
module_function
|
167
|
+
|
168
|
+
# Issues warning if cloud sdk client id is used
|
169
|
+
def warn_if_cloud_sdk_credentials client_id
|
170
|
+
return if ENV["GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS"]
|
171
|
+
warn CLOUD_SDK_CREDENTIALS_WARNING if client_id == CLOUD_SDK_CLIENT_ID
|
172
|
+
end
|
173
|
+
|
174
|
+
# Finds project_id from gcloud CLI configuration
|
175
|
+
def load_gcloud_project_id
|
176
|
+
gcloud = GCLOUD_WINDOWS_COMMAND if OS.windows?
|
177
|
+
gcloud = GCLOUD_POSIX_COMMAND unless OS.windows?
|
178
|
+
gcloud_json = IO.popen("#{gcloud} #{GCLOUD_CONFIG_COMMAND}", &:read)
|
179
|
+
config = MultiJson.load gcloud_json
|
180
|
+
config["configuration"]["properties"]["core"]["project"]
|
181
|
+
rescue StandardError
|
182
|
+
nil
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
def interpret_options scope, options
|
188
|
+
if scope.is_a? Hash
|
189
|
+
options = scope
|
190
|
+
scope = nil
|
191
|
+
end
|
192
|
+
return options.merge scope: scope if scope && !options[:scope]
|
193
|
+
options
|
194
|
+
end
|
195
|
+
|
196
|
+
def service_account_env_vars?
|
197
|
+
([PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR] - ENV.keys).empty? &&
|
198
|
+
!ENV.to_h.fetch_values(PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR).join(" ").empty?
|
199
|
+
end
|
200
|
+
|
201
|
+
def authorized_user_env_vars?
|
202
|
+
([CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR] - ENV.keys).empty? &&
|
203
|
+
!ENV.to_h.fetch_values(CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR).join(" ").empty?
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# Copyright 2015, Google Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are
|
6
|
+
# met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright
|
9
|
+
# notice, this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above
|
11
|
+
# copyright notice, this list of conditions and the following disclaimer
|
12
|
+
# in the documentation and/or other materials provided with the
|
13
|
+
# distribution.
|
14
|
+
# * Neither the name of Google Inc. nor the names of its
|
15
|
+
# contributors may be used to endorse or promote products derived from
|
16
|
+
# this software without specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
19
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
20
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
21
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
22
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
23
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
24
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
26
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
27
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
require "multi_json"
|
31
|
+
require "stringio"
|
32
|
+
|
33
|
+
require "googleauth/credentials_loader"
|
34
|
+
require "googleauth/service_account"
|
35
|
+
require "googleauth/user_refresh"
|
36
|
+
|
37
|
+
module Google
|
38
|
+
# Module Auth provides classes that provide Google-specific authorization
|
39
|
+
# used to access Google APIs.
|
40
|
+
module Auth
|
41
|
+
# DefaultCredentials is used to preload the credentials file, to determine
|
42
|
+
# which type of credentials should be loaded.
|
43
|
+
class DefaultCredentials
|
44
|
+
extend CredentialsLoader
|
45
|
+
|
46
|
+
# override CredentialsLoader#make_creds to use the class determined by
|
47
|
+
# loading the json.
|
48
|
+
def self.make_creds options = {}
|
49
|
+
json_key_io = options[:json_key_io]
|
50
|
+
if json_key_io
|
51
|
+
json_key, clz = determine_creds_class json_key_io
|
52
|
+
warn_if_cloud_sdk_credentials json_key["client_id"]
|
53
|
+
io = StringIO.new MultiJson.dump(json_key)
|
54
|
+
clz.make_creds options.merge(json_key_io: io)
|
55
|
+
else
|
56
|
+
warn_if_cloud_sdk_credentials ENV[CredentialsLoader::CLIENT_ID_VAR]
|
57
|
+
clz = read_creds
|
58
|
+
clz.make_creds options
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.read_creds
|
63
|
+
env_var = CredentialsLoader::ACCOUNT_TYPE_VAR
|
64
|
+
type = ENV[env_var]
|
65
|
+
raise "#{env_var} is undefined in env" unless type
|
66
|
+
case type
|
67
|
+
when "service_account"
|
68
|
+
ServiceAccountCredentials
|
69
|
+
when "authorized_user"
|
70
|
+
UserRefreshCredentials
|
71
|
+
else
|
72
|
+
raise "credentials type '#{type}' is not supported"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Reads the input json and determines which creds class to use.
|
77
|
+
def self.determine_creds_class json_key_io
|
78
|
+
json_key = MultiJson.load json_key_io.read
|
79
|
+
key = "type"
|
80
|
+
raise "the json is missing the '#{key}' field" unless json_key.key? key
|
81
|
+
type = json_key[key]
|
82
|
+
case type
|
83
|
+
when "service_account"
|
84
|
+
[json_key, ServiceAccountCredentials]
|
85
|
+
when "authorized_user"
|
86
|
+
[json_key, UserRefreshCredentials]
|
87
|
+
else
|
88
|
+
raise "credentials type '#{type}' is not supported"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Copyright 2015, Google Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are
|
6
|
+
# met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright
|
9
|
+
# notice, this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above
|
11
|
+
# copyright notice, this list of conditions and the following disclaimer
|
12
|
+
# in the documentation and/or other materials provided with the
|
13
|
+
# distribution.
|
14
|
+
# * Neither the name of Google Inc. nor the names of its
|
15
|
+
# contributors may be used to endorse or promote products derived from
|
16
|
+
# this software without specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
19
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
20
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
21
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
22
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
23
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
24
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
26
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
27
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
require "googleauth/signet"
|
31
|
+
require "googleauth/credentials_loader"
|
32
|
+
require "multi_json"
|
33
|
+
|
34
|
+
module Google
|
35
|
+
# Module Auth provides classes that provide Google-specific authorization
|
36
|
+
# used to access Google APIs.
|
37
|
+
module Auth
|
38
|
+
# Authenticates requests using IAM credentials.
|
39
|
+
class IAMCredentials
|
40
|
+
SELECTOR_KEY = "x-goog-iam-authority-selector".freeze
|
41
|
+
TOKEN_KEY = "x-goog-iam-authorization-token".freeze
|
42
|
+
|
43
|
+
# Initializes an IAMCredentials.
|
44
|
+
#
|
45
|
+
# @param selector the IAM selector.
|
46
|
+
# @param token the IAM token.
|
47
|
+
def initialize selector, token
|
48
|
+
raise TypeError unless selector.is_a? String
|
49
|
+
raise TypeError unless token.is_a? String
|
50
|
+
@selector = selector
|
51
|
+
@token = token
|
52
|
+
end
|
53
|
+
|
54
|
+
# Adds the credential fields to the hash.
|
55
|
+
def apply! a_hash
|
56
|
+
a_hash[SELECTOR_KEY] = @selector
|
57
|
+
a_hash[TOKEN_KEY] = @token
|
58
|
+
a_hash
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns a clone of a_hash updated with the authoriation header
|
62
|
+
def apply a_hash
|
63
|
+
a_copy = a_hash.clone
|
64
|
+
apply! a_copy
|
65
|
+
a_copy
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns a reference to the #apply method, suitable for passing as
|
69
|
+
# a closure
|
70
|
+
def updater_proc
|
71
|
+
proc { |a_hash, _opts = {}| apply a_hash }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -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
|