blob: e78add417eb258b144f7825f52623895afc55059 [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
36 if id_info['iss'] != 'https://accounts.google.com':
37 raise ValueError('Wrong issuer.')
38
39 userid = id_info['sub']
40
41By default, this will re-fetch certificates for each verification. Because
42Google's public keys are only changed infrequently (on the order of once per
43day), you may wish to take advantage of caching to reduce latency and the
44potential for network errors. This can be accomplished using an external
45library like `CacheControl`_ to create a cache-aware
46:class:`google.auth.transport.Request`::
47
48 import cachecontrol
49 import google.auth.transport.requests
50 import requests
51
52 session = requests.session()
53 cached_session = cachecontrol.CacheControl(session)
54 request = google.auth.transport.requests.Request(session=cached_session)
55
56.. _OpenID Connect ID Token:
57 http://openid.net/specs/openid-connect-core-1_0.html#IDToken
58.. _CacheControl: https://cachecontrol.readthedocs.io
59"""
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080060
61import json
arithmetic1728506c5652020-04-01 10:34:37 -070062import os
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080063
arithmetic1728506c5652020-04-01 10:34:37 -070064import six
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080065from six.moves import http_client
66
arithmetic1728506c5652020-04-01 10:34:37 -070067from google.auth import environment_vars
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080068from google.auth import exceptions
69from google.auth import jwt
70
arithmetic1728506c5652020-04-01 10:34:37 -070071
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080072# The URL that provides public certificates for verifying ID tokens issued
73# by Google's OAuth 2.0 authorization server.
k-yone901c2592020-02-15 02:44:04 +090074_GOOGLE_OAUTH2_CERTS_URL = "https://www.googleapis.com/oauth2/v1/certs"
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080075
76# The URL that provides public certificates for verifying ID tokens issued
77# by Firebase and the Google APIs infrastructure
78_GOOGLE_APIS_CERTS_URL = (
Bu Sun Kim9eec0912019-10-21 17:04:21 -070079 "https://www.googleapis.com/robot/v1/metadata/x509"
80 "/securetoken@system.gserviceaccount.com"
81)
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -080082
83
84def _fetch_certs(request, certs_url):
85 """Fetches certificates.
86
87 Google-style cerificate endpoints return JSON in the format of
88 ``{'key id': 'x509 certificate'}``.
89
90 Args:
91 request (google.auth.transport.Request): The object used to make
92 HTTP requests.
93 certs_url (str): The certificate endpoint URL.
94
95 Returns:
96 Mapping[str, str]: A mapping of public key ID to x.509 certificate
97 data.
98 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -070099 response = request(certs_url, method="GET")
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800100
101 if response.status != http_client.OK:
102 raise exceptions.TransportError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700103 "Could not fetch certificates at {}".format(certs_url)
104 )
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800105
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700106 return json.loads(response.data.decode("utf-8"))
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800107
108
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700109def verify_token(id_token, request, audience=None, certs_url=_GOOGLE_OAUTH2_CERTS_URL):
Jon Wayne Parrotte2ab1002016-11-10 15:11:48 -0800110 """Verifies an ID token and returns the decoded token.
111
112 Args:
113 id_token (Union[str, bytes]): The encoded token.
114 request (google.auth.transport.Request): The object used to make
115 HTTP requests.
116 audience (str): The audience that this token is intended for. If None
117 then the audience is not verified.
118 certs_url (str): The URL that specifies the certificates to use to
119 verify the token. This URL should return JSON in the format of
120 ``{'key id': 'x509 certificate'}``.
121
122 Returns:
123 Mapping[str, Any]: The decoded token.
124 """
125 certs = _fetch_certs(request, certs_url)
126
127 return jwt.decode(id_token, certs=certs, audience=audience)
128
129
130def verify_oauth2_token(id_token, request, audience=None):
131 """Verifies an ID Token issued by Google's OAuth 2.0 authorization server.
132
133 Args:
134 id_token (Union[str, bytes]): The encoded token.
135 request (google.auth.transport.Request): The object used to make
136 HTTP requests.
137 audience (str): The audience that this token is intended for. This is
138 typically your application's OAuth 2.0 client ID. If None then the
139 audience is not verified.
140
141 Returns:
142 Mapping[str, Any]: The decoded token.
143 """
144 return 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
148
149def verify_firebase_token(id_token, request, audience=None):
150 """Verifies an ID Token issued by Firebase Authentication.
151
152 Args:
153 id_token (Union[str, bytes]): The encoded token.
154 request (google.auth.transport.Request): The object used to make
155 HTTP requests.
156 audience (str): The audience that this token is intended for. This is
157 typically your Firebase application ID. If None then the audience
158 is not verified.
159
160 Returns:
161 Mapping[str, Any]: The decoded token.
162 """
163 return verify_token(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700164 id_token, request, audience=audience, certs_url=_GOOGLE_APIS_CERTS_URL
165 )
arithmetic1728506c5652020-04-01 10:34:37 -0700166
167
168def fetch_id_token(request, audience):
169 """Fetch the ID Token from the current environment.
170
171 This function acquires ID token from the environment in the following order:
172
173 1. If the application is running in Compute Engine, App Engine or Cloud Run,
174 then the ID token are obtained from the metadata server.
175 2. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
176 to the path of a valid service account JSON file, then ID token is
177 acquired using this service account credentials.
178 3. If metadata server doesn't exist and no valid service account credentials
179 are found, :class:`~google.auth.exceptions.DefaultCredentialsError` will
180 be raised.
181
182 Example::
183
184 import google.oauth2.id_token
185 import google.auth.transport.requests
186
187 request = google.auth.transport.requests.Request()
188 target_audience = "https://pubsub.googleapis.com"
189
190 id_token = google.oauth2.id_token.fetch_id_token(request, target_audience)
191
192 Args:
193 request (google.auth.transport.Request): A callable used to make
194 HTTP requests.
195 audience (str): The audience that this ID token is intended for.
196
197 Returns:
198 str: The ID token.
199
200 Raises:
201 ~google.auth.exceptions.DefaultCredentialsError:
202 If metadata server doesn't exist and no valid service account
203 credentials are found.
204 """
205 # 1. First try to fetch ID token from metada server if it exists. The code
206 # works for GAE and Cloud Run metadata server as well.
207 try:
208 from google.auth import compute_engine
209
210 credentials = compute_engine.IDTokenCredentials(
211 request, audience, use_metadata_identity_endpoint=True
212 )
213 credentials.refresh(request)
214 return credentials.token
arithmetic17283d672e92020-05-11 14:22:32 -0700215 except (ImportError, exceptions.TransportError, exceptions.RefreshError):
arithmetic1728506c5652020-04-01 10:34:37 -0700216 pass
217
218 # 2. Try to use service account credentials to get ID token.
219
220 # Try to get credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
221 # variable.
222 credentials_filename = os.environ.get(environment_vars.CREDENTIALS)
223 if not (
224 credentials_filename
225 and os.path.exists(credentials_filename)
226 and os.path.isfile(credentials_filename)
227 ):
228 raise exceptions.DefaultCredentialsError(
229 "Neither metadata server or valid service account credentials are found."
230 )
231
232 try:
233 with open(credentials_filename, "r") as f:
234 info = json.load(f)
235 credentials_content = (
236 (info.get("type") == "service_account") and info or None
237 )
238
239 from google.oauth2 import service_account
240
241 credentials = service_account.IDTokenCredentials.from_service_account_info(
242 credentials_content, target_audience=audience
243 )
244 except ValueError as caught_exc:
245 new_exc = exceptions.DefaultCredentialsError(
246 "Neither metadata server or valid service account credentials are found.",
247 caught_exc,
248 )
249 six.raise_from(new_exc, caught_exc)
250
251 credentials.refresh(request)
252 return credentials.token