blob: 25492ca6c19824ee79eaff92023598abfe5fb165 [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
Tres Seaver560cf1e2021-08-03 16:35:54 -040058import http.client
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080059import json
arithmetic1728506c5652020-04-01 10:34:37 -070060import os
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080061
arithmetic1728506c5652020-04-01 10:34:37 -070062from google.auth import environment_vars
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080063from google.auth import exceptions
64from google.auth import jwt
65
arithmetic1728506c5652020-04-01 10:34:37 -070066
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080067# The URL that provides public certificates for verifying ID tokens issued
68# by Google's OAuth 2.0 authorization server.
k-yone901c2592020-02-15 02:44:04 +090069_GOOGLE_OAUTH2_CERTS_URL = "https://www.googleapis.com/oauth2/v1/certs"
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080070
71# The URL that provides public certificates for verifying ID tokens issued
72# by Firebase and the Google APIs infrastructure
73_GOOGLE_APIS_CERTS_URL = (
Bu Sun Kim9eec0912019-10-21 17:04:21 -070074 "https://www.googleapis.com/robot/v1/metadata/x509"
75 "/securetoken@system.gserviceaccount.com"
76)
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080077
Bu Sun Kimc05b8b52020-06-29 16:27:30 -070078_GOOGLE_ISSUERS = ["accounts.google.com", "https://accounts.google.com"]
79
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080080
81def _fetch_certs(request, certs_url):
82 """Fetches certificates.
83
84 Google-style cerificate endpoints return JSON in the format of
85 ``{'key id': 'x509 certificate'}``.
86
87 Args:
88 request (google.auth.transport.Request): The object used to make
89 HTTP requests.
90 certs_url (str): The certificate endpoint URL.
91
92 Returns:
93 Mapping[str, str]: A mapping of public key ID to x.509 certificate
94 data.
95 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -070096 response = request(certs_url, method="GET")
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080097
Tres Seaver560cf1e2021-08-03 16:35:54 -040098 if response.status != http.client.OK:
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080099 raise exceptions.TransportError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700100 "Could not fetch certificates at {}".format(certs_url)
101 )
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800102
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700103 return json.loads(response.data.decode("utf-8"))
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800104
105
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700106def verify_token(id_token, request, audience=None, certs_url=_GOOGLE_OAUTH2_CERTS_URL):
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800107 """Verifies an ID token and returns the decoded token.
108
109 Args:
110 id_token (Union[str, bytes]): The encoded token.
111 request (google.auth.transport.Request): The object used to make
112 HTTP requests.
Jonathan Beaulieu56c39462021-04-15 04:28:04 -0400113 audience (str or list): The audience or audiences that this token is
114 intended for. If None then the audience is not verified.
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800115 certs_url (str): The URL that specifies the certificates to use to
116 verify the token. This URL should return JSON in the format of
117 ``{'key id': 'x509 certificate'}``.
118
119 Returns:
120 Mapping[str, Any]: The decoded token.
121 """
122 certs = _fetch_certs(request, certs_url)
123
124 return jwt.decode(id_token, certs=certs, audience=audience)
125
126
127def verify_oauth2_token(id_token, request, audience=None):
128 """Verifies an ID Token issued by Google's OAuth 2.0 authorization server.
129
130 Args:
131 id_token (Union[str, bytes]): The encoded token.
132 request (google.auth.transport.Request): The object used to make
133 HTTP requests.
134 audience (str): The audience that this token is intended for. This is
135 typically your application's OAuth 2.0 client ID. If None then the
136 audience is not verified.
137
138 Returns:
139 Mapping[str, Any]: The decoded token.
Bu Sun Kimc05b8b52020-06-29 16:27:30 -0700140
141 Raises:
142 exceptions.GoogleAuthError: If the issuer is invalid.
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800143 """
Bu Sun Kimc05b8b52020-06-29 16:27:30 -0700144 idinfo = verify_token(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700145 id_token, request, audience=audience, certs_url=_GOOGLE_OAUTH2_CERTS_URL
146 )
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800147
Bu Sun Kimc05b8b52020-06-29 16:27:30 -0700148 if idinfo["iss"] not in _GOOGLE_ISSUERS:
149 raise exceptions.GoogleAuthError(
150 "Wrong issuer. 'iss' should be one of the following: {}".format(
151 _GOOGLE_ISSUERS
152 )
153 )
154
155 return idinfo
156
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800157
158def verify_firebase_token(id_token, request, audience=None):
159 """Verifies an ID Token issued by Firebase Authentication.
160
161 Args:
162 id_token (Union[str, bytes]): The encoded token.
163 request (google.auth.transport.Request): The object used to make
164 HTTP requests.
165 audience (str): The audience that this token is intended for. This is
166 typically your Firebase application ID. If None then the audience
167 is not verified.
168
169 Returns:
170 Mapping[str, Any]: The decoded token.
171 """
172 return verify_token(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700173 id_token, request, audience=audience, certs_url=_GOOGLE_APIS_CERTS_URL
174 )
arithmetic1728506c5652020-04-01 10:34:37 -0700175
176
177def fetch_id_token(request, audience):
178 """Fetch the ID Token from the current environment.
179
arithmetic1728c34452e2021-07-14 11:40:05 -0700180 This function acquires ID token from the environment in the following order.
181 See https://google.aip.dev/auth/4110.
arithmetic1728506c5652020-04-01 10:34:37 -0700182
arithmetic1728c34452e2021-07-14 11:40:05 -0700183 1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
arithmetic1728506c5652020-04-01 10:34:37 -0700184 to the path of a valid service account JSON file, then ID token is
185 acquired using this service account credentials.
arithmetic1728c34452e2021-07-14 11:40:05 -0700186 2. If the application is running in Compute Engine, App Engine or Cloud Run,
187 then the ID token are obtained from the metadata server.
arithmetic1728506c5652020-04-01 10:34:37 -0700188 3. If metadata server doesn't exist and no valid service account credentials
189 are found, :class:`~google.auth.exceptions.DefaultCredentialsError` will
190 be raised.
191
192 Example::
193
194 import google.oauth2.id_token
195 import google.auth.transport.requests
196
197 request = google.auth.transport.requests.Request()
198 target_audience = "https://pubsub.googleapis.com"
199
200 id_token = google.oauth2.id_token.fetch_id_token(request, target_audience)
201
202 Args:
203 request (google.auth.transport.Request): A callable used to make
204 HTTP requests.
205 audience (str): The audience that this ID token is intended for.
206
207 Returns:
208 str: The ID token.
209
210 Raises:
211 ~google.auth.exceptions.DefaultCredentialsError:
212 If metadata server doesn't exist and no valid service account
213 credentials are found.
214 """
arithmetic1728c34452e2021-07-14 11:40:05 -0700215 # 1. Try to get credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
arithmetic1728506c5652020-04-01 10:34:37 -0700216 # variable.
217 credentials_filename = os.environ.get(environment_vars.CREDENTIALS)
arithmetic1728c34452e2021-07-14 11:40:05 -0700218 if credentials_filename:
219 if not (
220 os.path.exists(credentials_filename)
221 and os.path.isfile(credentials_filename)
222 ):
223 raise exceptions.DefaultCredentialsError(
224 "GOOGLE_APPLICATION_CREDENTIALS path is either not found or invalid."
225 )
arithmetic1728506c5652020-04-01 10:34:37 -0700226
arithmetic1728c34452e2021-07-14 11:40:05 -0700227 try:
228 with open(credentials_filename, "r") as f:
229 from google.oauth2 import service_account
230
231 info = json.load(f)
232 if info.get("type") == "service_account":
233 credentials = service_account.IDTokenCredentials.from_service_account_info(
234 info, target_audience=audience
235 )
236 credentials.refresh(request)
237 return credentials.token
238 except ValueError as caught_exc:
239 new_exc = exceptions.DefaultCredentialsError(
240 "GOOGLE_APPLICATION_CREDENTIALS is not valid service account credentials.",
241 caught_exc,
242 )
Tres Seaver560cf1e2021-08-03 16:35:54 -0400243 raise new_exc from caught_exc
arithmetic1728c34452e2021-07-14 11:40:05 -0700244
Tres Seaver560cf1e2021-08-03 16:35:54 -0400245 # 2. Try to fetch ID token from metada server if it exists. The code
246 # works for GAE and Cloud Run metadata server as well.
arithmetic1728506c5652020-04-01 10:34:37 -0700247 try:
arithmetic1728c34452e2021-07-14 11:40:05 -0700248 from google.auth import compute_engine
249 from google.auth.compute_engine import _metadata
250
251 if _metadata.ping(request):
252 credentials = compute_engine.IDTokenCredentials(
253 request, audience, use_metadata_identity_endpoint=True
arithmetic1728506c5652020-04-01 10:34:37 -0700254 )
arithmetic1728c34452e2021-07-14 11:40:05 -0700255 credentials.refresh(request)
256 return credentials.token
257 except (ImportError, exceptions.TransportError):
258 pass
arithmetic1728506c5652020-04-01 10:34:37 -0700259
arithmetic1728c34452e2021-07-14 11:40:05 -0700260 raise exceptions.DefaultCredentialsError(
261 "Neither metadata server or valid service account credentials are found."
262 )