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.
Files changed (78) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +7 -0
  3. data/.github/CONTRIBUTING.md +74 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +36 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
  6. data/.github/ISSUE_TEMPLATE/support_request.md +7 -0
  7. data/.github/renovate.json +6 -0
  8. data/.github/sync-repo-settings.yaml +18 -0
  9. data/.github/workflows/ci.yml +55 -0
  10. data/.github/workflows/release-please.yml +39 -0
  11. data/.gitignore +3 -0
  12. data/.kokoro/populate-secrets.sh +76 -0
  13. data/.kokoro/release.cfg +52 -0
  14. data/.kokoro/release.sh +18 -0
  15. data/.kokoro/trampoline_v2.sh +489 -0
  16. data/.repo-metadata.json +5 -0
  17. data/.rubocop.yml +17 -0
  18. data/.toys/.toys.rb +45 -0
  19. data/.toys/ci.rb +43 -0
  20. data/.toys/kokoro/.toys.rb +66 -0
  21. data/.toys/kokoro/publish-docs.rb +67 -0
  22. data/.toys/kokoro/publish-gem.rb +53 -0
  23. data/.toys/linkinator.rb +43 -0
  24. data/.trampolinerc +48 -0
  25. data/CHANGELOG.md +199 -0
  26. data/CODE_OF_CONDUCT.md +43 -0
  27. data/Gemfile +22 -1
  28. data/{COPYING → LICENSE} +0 -0
  29. data/README.md +140 -17
  30. data/googleauth.gemspec +28 -28
  31. data/integration/helper.rb +31 -0
  32. data/integration/id_tokens/key_source_test.rb +74 -0
  33. data/lib/googleauth.rb +7 -37
  34. data/lib/googleauth/application_default.rb +81 -0
  35. data/lib/googleauth/client_id.rb +104 -0
  36. data/lib/googleauth/compute_engine.rb +73 -26
  37. data/lib/googleauth/credentials.rb +561 -0
  38. data/lib/googleauth/credentials_loader.rb +207 -0
  39. data/lib/googleauth/default_credentials.rb +93 -0
  40. data/lib/googleauth/iam.rb +75 -0
  41. data/lib/googleauth/id_tokens.rb +233 -0
  42. data/lib/googleauth/id_tokens/errors.rb +71 -0
  43. data/lib/googleauth/id_tokens/key_sources.rb +396 -0
  44. data/lib/googleauth/id_tokens/verifier.rb +142 -0
  45. data/lib/googleauth/json_key_reader.rb +50 -0
  46. data/lib/googleauth/scope_util.rb +61 -0
  47. data/lib/googleauth/service_account.rb +177 -67
  48. data/lib/googleauth/signet.rb +69 -8
  49. data/lib/googleauth/stores/file_token_store.rb +65 -0
  50. data/lib/googleauth/stores/redis_token_store.rb +96 -0
  51. data/lib/googleauth/token_store.rb +69 -0
  52. data/lib/googleauth/user_authorizer.rb +285 -0
  53. data/lib/googleauth/user_refresh.rb +129 -0
  54. data/lib/googleauth/version.rb +1 -1
  55. data/lib/googleauth/web_user_authorizer.rb +295 -0
  56. data/spec/googleauth/apply_auth_examples.rb +96 -94
  57. data/spec/googleauth/client_id_spec.rb +160 -0
  58. data/spec/googleauth/compute_engine_spec.rb +125 -55
  59. data/spec/googleauth/credentials_spec.rb +600 -0
  60. data/spec/googleauth/get_application_default_spec.rb +232 -80
  61. data/spec/googleauth/iam_spec.rb +80 -0
  62. data/spec/googleauth/scope_util_spec.rb +77 -0
  63. data/spec/googleauth/service_account_spec.rb +422 -68
  64. data/spec/googleauth/signet_spec.rb +101 -25
  65. data/spec/googleauth/stores/file_token_store_spec.rb +57 -0
  66. data/spec/googleauth/stores/redis_token_store_spec.rb +50 -0
  67. data/spec/googleauth/stores/store_examples.rb +58 -0
  68. data/spec/googleauth/user_authorizer_spec.rb +343 -0
  69. data/spec/googleauth/user_refresh_spec.rb +359 -0
  70. data/spec/googleauth/web_user_authorizer_spec.rb +172 -0
  71. data/spec/spec_helper.rb +51 -10
  72. data/test/helper.rb +33 -0
  73. data/test/id_tokens/key_sources_test.rb +240 -0
  74. data/test/id_tokens/verifier_test.rb +269 -0
  75. metadata +114 -75
  76. data/.travis.yml +0 -18
  77. data/CONTRIBUTING.md +0 -32
  78. 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