blob: 20d3ac1aff7fd48e2f8f746412b7991441028eb8 [file] [log] [blame]
C.J. Collier37141e42020-02-13 13:49:49 -08001# Copyright 2016 Google LLC
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -08002#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Arlan Jaska4255b102018-02-08 15:51:26 -080015"""Google ID Token helpers.
16
17Provides support for verifying `OpenID Connect ID Tokens`_, especially ones
18generated by Google infrastructure.
19
20To parse and verify an ID Token issued by Google's OAuth 2.0 authorization
21server use :func:`verify_oauth2_token`. To verify an ID Token issued by
22Firebase, use :func:`verify_firebase_token`.
23
24A general purpose ID Token verifier is available as :func:`verify_token`.
25
26Example::
27
28 from google.oauth2 import id_token
29 from google.auth.transport import requests
30
31 request = requests.Request()
32
33 id_info = id_token.verify_oauth2_token(
34 token, request, 'my-client-id.example.com')
35
Arlan Jaska4255b102018-02-08 15:51:26 -080036 userid = id_info['sub']
37
38By default, this will re-fetch certificates for each verification. Because
39Google's public keys are only changed infrequently (on the order of once per
40day), you may wish to take advantage of caching to reduce latency and the
41potential for network errors. This can be accomplished using an external
42library like `CacheControl`_ to create a cache-aware
43:class:`google.auth.transport.Request`::
44
45 import cachecontrol
46 import google.auth.transport.requests
47 import requests
48
49 session = requests.session()
50 cached_session = cachecontrol.CacheControl(session)
51 request = google.auth.transport.requests.Request(session=cached_session)
52
Bu Sun Kimb1a12d22021-02-26 11:50:02 -070053.. _OpenID Connect ID Tokens:
Arlan Jaska4255b102018-02-08 15:51:26 -080054 http://openid.net/specs/openid-connect-core-1_0.html#IDToken
55.. _CacheControl: https://cachecontrol.readthedocs.io
56"""
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080057
58import json
arithmetic1728506c5652020-04-01 10:34:37 -070059import os
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080060
arithmetic17285bd5ccf2021-10-21 15:25:46 -070061import six
62from six.moves import http_client
63
arithmetic1728506c5652020-04-01 10:34:37 -070064from google.auth import environment_vars
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080065from google.auth import exceptions
66from google.auth import jwt
67
arithmetic1728506c5652020-04-01 10:34:37 -070068
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080069# The URL that provides public certificates for verifying ID tokens issued
70# by Google's OAuth 2.0 authorization server.
k-yone901c2592020-02-15 02:44:04 +090071_GOOGLE_OAUTH2_CERTS_URL = "https://www.googleapis.com/oauth2/v1/certs"
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080072
73# The URL that provides public certificates for verifying ID tokens issued
74# by Firebase and the Google APIs infrastructure
75_GOOGLE_APIS_CERTS_URL = (
Bu Sun Kim9eec0912019-10-21 17:04:21 -070076 "https://www.googleapis.com/robot/v1/metadata/x509"
77 "/securetoken@system.gserviceaccount.com"
78)
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080079
Bu Sun Kimc05b8b52020-06-29 16:27:30 -070080_GOOGLE_ISSUERS = ["accounts.google.com", "https://accounts.google.com"]
81
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080082
83def _fetch_certs(request, certs_url):
84 """Fetches certificates.
85
86 Google-style cerificate endpoints return JSON in the format of
87 ``{'key id': 'x509 certificate'}``.
88
89 Args:
90 request (google.auth.transport.Request): The object used to make
91 HTTP requests.
92 certs_url (str): The certificate endpoint URL.
93
94 Returns:
95 Mapping[str, str]: A mapping of public key ID to x.509 certificate
96 data.
97 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -070098 response = request(certs_url, method="GET")
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080099
arithmetic17285bd5ccf2021-10-21 15:25:46 -0700100 if response.status != http_client.OK:
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800101 raise exceptions.TransportError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700102 "Could not fetch certificates at {}".format(certs_url)
103 )
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800104
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700105 return json.loads(response.data.decode("utf-8"))
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800106
107
arithmetic17288e95c1e2021-10-25 16:31:47 -0700108def verify_token(
109 id_token,
110 request,
111 audience=None,
112 certs_url=_GOOGLE_OAUTH2_CERTS_URL,
113 clock_skew_in_seconds=0,
114):
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800115 """Verifies an ID token and returns the decoded token.
116
117 Args:
118 id_token (Union[str, bytes]): The encoded token.
119 request (google.auth.transport.Request): The object used to make
120 HTTP requests.
Jonathan Beaulieu56c39462021-04-15 04:28:04 -0400121 audience (str or list): The audience or audiences that this token is
122 intended for. If None then the audience is not verified.
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800123 certs_url (str): The URL that specifies the certificates to use to
124 verify the token. This URL should return JSON in the format of
125 ``{'key id': 'x509 certificate'}``.
arithmetic17288e95c1e2021-10-25 16:31:47 -0700126 clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
127 validation.
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800128
129 Returns:
130 Mapping[str, Any]: The decoded token.
131 """
132 certs = _fetch_certs(request, certs_url)
133
arithmetic17288e95c1e2021-10-25 16:31:47 -0700134 return jwt.decode(
135 id_token,
136 certs=certs,
137 audience=audience,
138 clock_skew_in_seconds=clock_skew_in_seconds,
139 )
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800140
141
arithmetic17288e95c1e2021-10-25 16:31:47 -0700142def verify_oauth2_token(id_token, request, audience=None, clock_skew_in_seconds=0):
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800143 """Verifies an ID Token issued by Google's OAuth 2.0 authorization server.
144
145 Args:
146 id_token (Union[str, bytes]): The encoded token.
147 request (google.auth.transport.Request): The object used to make
148 HTTP requests.
149 audience (str): The audience that this token is intended for. This is
150 typically your application's OAuth 2.0 client ID. If None then the
151 audience is not verified.
arithmetic17288e95c1e2021-10-25 16:31:47 -0700152 clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
153 validation.
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800154
155 Returns:
156 Mapping[str, Any]: The decoded token.
Bu Sun Kimc05b8b52020-06-29 16:27:30 -0700157
158 Raises:
159 exceptions.GoogleAuthError: If the issuer is invalid.
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800160 """
Bu Sun Kimc05b8b52020-06-29 16:27:30 -0700161 idinfo = verify_token(
arithmetic17288e95c1e2021-10-25 16:31:47 -0700162 id_token,
163 request,
164 audience=audience,
165 certs_url=_GOOGLE_OAUTH2_CERTS_URL,
166 clock_skew_in_seconds=clock_skew_in_seconds,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700167 )
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800168
Bu Sun Kimc05b8b52020-06-29 16:27:30 -0700169 if idinfo["iss"] not in _GOOGLE_ISSUERS:
170 raise exceptions.GoogleAuthError(
171 "Wrong issuer. 'iss' should be one of the following: {}".format(
172 _GOOGLE_ISSUERS
173 )
174 )
175
176 return idinfo
177
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800178
arithmetic17288e95c1e2021-10-25 16:31:47 -0700179def verify_firebase_token(id_token, request, audience=None, clock_skew_in_seconds=0):
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800180 """Verifies an ID Token issued by Firebase Authentication.
181
182 Args:
183 id_token (Union[str, bytes]): The encoded token.
184 request (google.auth.transport.Request): The object used to make
185 HTTP requests.
186 audience (str): The audience that this token is intended for. This is
187 typically your Firebase application ID. If None then the audience
188 is not verified.
arithmetic17288e95c1e2021-10-25 16:31:47 -0700189 clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
190 validation.
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800191
192 Returns:
193 Mapping[str, Any]: The decoded token.
194 """
195 return verify_token(
arithmetic17288e95c1e2021-10-25 16:31:47 -0700196 id_token,
197 request,
198 audience=audience,
199 certs_url=_GOOGLE_APIS_CERTS_URL,
200 clock_skew_in_seconds=clock_skew_in_seconds,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700201 )
arithmetic1728506c5652020-04-01 10:34:37 -0700202
203
204def fetch_id_token(request, audience):
205 """Fetch the ID Token from the current environment.
206
arithmetic1728c34452e2021-07-14 11:40:05 -0700207 This function acquires ID token from the environment in the following order.
208 See https://google.aip.dev/auth/4110.
arithmetic1728506c5652020-04-01 10:34:37 -0700209
arithmetic1728c34452e2021-07-14 11:40:05 -0700210 1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
arithmetic1728506c5652020-04-01 10:34:37 -0700211 to the path of a valid service account JSON file, then ID token is
212 acquired using this service account credentials.
arithmetic1728c34452e2021-07-14 11:40:05 -0700213 2. If the application is running in Compute Engine, App Engine or Cloud Run,
214 then the ID token are obtained from the metadata server.
arithmetic1728506c5652020-04-01 10:34:37 -0700215 3. If metadata server doesn't exist and no valid service account credentials
216 are found, :class:`~google.auth.exceptions.DefaultCredentialsError` will
217 be raised.
218
219 Example::
220
221 import google.oauth2.id_token
222 import google.auth.transport.requests
223
224 request = google.auth.transport.requests.Request()
225 target_audience = "https://pubsub.googleapis.com"
226
227 id_token = google.oauth2.id_token.fetch_id_token(request, target_audience)
228
229 Args:
230 request (google.auth.transport.Request): A callable used to make
231 HTTP requests.
232 audience (str): The audience that this ID token is intended for.
233
234 Returns:
235 str: The ID token.
236
237 Raises:
238 ~google.auth.exceptions.DefaultCredentialsError:
239 If metadata server doesn't exist and no valid service account
240 credentials are found.
241 """
arithmetic1728c34452e2021-07-14 11:40:05 -0700242 # 1. Try to get credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
arithmetic1728506c5652020-04-01 10:34:37 -0700243 # variable.
244 credentials_filename = os.environ.get(environment_vars.CREDENTIALS)
arithmetic1728c34452e2021-07-14 11:40:05 -0700245 if credentials_filename:
246 if not (
247 os.path.exists(credentials_filename)
248 and os.path.isfile(credentials_filename)
249 ):
250 raise exceptions.DefaultCredentialsError(
251 "GOOGLE_APPLICATION_CREDENTIALS path is either not found or invalid."
252 )
arithmetic1728506c5652020-04-01 10:34:37 -0700253
arithmetic1728c34452e2021-07-14 11:40:05 -0700254 try:
255 with open(credentials_filename, "r") as f:
256 from google.oauth2 import service_account
257
258 info = json.load(f)
259 if info.get("type") == "service_account":
260 credentials = service_account.IDTokenCredentials.from_service_account_info(
261 info, target_audience=audience
262 )
263 credentials.refresh(request)
264 return credentials.token
265 except ValueError as caught_exc:
266 new_exc = exceptions.DefaultCredentialsError(
267 "GOOGLE_APPLICATION_CREDENTIALS is not valid service account credentials.",
268 caught_exc,
269 )
arithmetic17285bd5ccf2021-10-21 15:25:46 -0700270 six.raise_from(new_exc, caught_exc)
arithmetic1728c34452e2021-07-14 11:40:05 -0700271
Tres Seaver560cf1e2021-08-03 16:35:54 -0400272 # 2. Try to fetch ID token from metada server if it exists. The code
273 # works for GAE and Cloud Run metadata server as well.
arithmetic1728506c5652020-04-01 10:34:37 -0700274 try:
arithmetic1728c34452e2021-07-14 11:40:05 -0700275 from google.auth import compute_engine
276 from google.auth.compute_engine import _metadata
277
278 if _metadata.ping(request):
279 credentials = compute_engine.IDTokenCredentials(
280 request, audience, use_metadata_identity_endpoint=True
arithmetic1728506c5652020-04-01 10:34:37 -0700281 )
arithmetic1728c34452e2021-07-14 11:40:05 -0700282 credentials.refresh(request)
283 return credentials.token
284 except (ImportError, exceptions.TransportError):
285 pass
arithmetic1728506c5652020-04-01 10:34:37 -0700286
arithmetic1728c34452e2021-07-14 11:40:05 -0700287 raise exceptions.DefaultCredentialsError(
288 "Neither metadata server or valid service account credentials are found."
289 )