@@ -217,12 +217,18 @@ def __init__(self, app):
217
217
project_id = app .project_id , short_name = 'ID token' ,
218
218
operation = 'verify_id_token()' ,
219
219
doc_url = 'https://firebase.google.com/docs/auth/admin/verify-id-tokens' ,
220
- cert_url = ID_TOKEN_CERT_URI , issuer = ID_TOKEN_ISSUER_PREFIX )
220
+ cert_url = ID_TOKEN_CERT_URI ,
221
+ issuer = ID_TOKEN_ISSUER_PREFIX ,
222
+ invalid_token_error = _auth_utils .InvalidIdTokenError ,
223
+ expired_token_error = ExpiredIdTokenError )
221
224
self .cookie_verifier = _JWTVerifier (
222
225
project_id = app .project_id , short_name = 'session cookie' ,
223
226
operation = 'verify_session_cookie()' ,
224
227
doc_url = 'https://firebase.google.com/docs/auth/admin/verify-id-tokens' ,
225
- cert_url = COOKIE_CERT_URI , issuer = COOKIE_ISSUER_PREFIX )
228
+ cert_url = COOKIE_CERT_URI ,
229
+ issuer = COOKIE_ISSUER_PREFIX ,
230
+ invalid_token_error = InvalidSessionCookieError ,
231
+ expired_token_error = ExpiredSessionCookieError )
226
232
227
233
def verify_id_token (self , id_token ):
228
234
return self .id_token_verifier .verify (id_token , self .request )
@@ -245,6 +251,8 @@ def __init__(self, **kwargs):
245
251
self .articled_short_name = 'an {0}' .format (self .short_name )
246
252
else :
247
253
self .articled_short_name = 'a {0}' .format (self .short_name )
254
+ self .invalid_token_error = kwargs .pop ('invalid_token_error' )
255
+ self .expired_token_error = kwargs .pop ('expired_token_error' )
248
256
249
257
def verify (self , token , request ):
250
258
"""Verifies the signature and data for the provided JWT."""
@@ -261,8 +269,7 @@ def verify(self, token, request):
261
269
'or set your Firebase project ID as an app option. Alternatively set the '
262
270
'GOOGLE_CLOUD_PROJECT environment variable.' .format (self .operation ))
263
271
264
- header = jwt .decode_header (token )
265
- payload = jwt .decode (token , verify = False )
272
+ header , payload = self ._decode_unverified (token )
266
273
issuer = payload .get ('iss' )
267
274
audience = payload .get ('aud' )
268
275
subject = payload .get ('sub' )
@@ -275,12 +282,12 @@ def verify(self, token, request):
275
282
'See {0} for details on how to retrieve {1}.' .format (self .url , self .short_name ))
276
283
277
284
error_message = None
278
- if not header . get ( 'kid' ) :
279
- if audience == FIREBASE_AUDIENCE :
280
- error_message = (
281
- '{0} expects {1}, but was given a custom '
282
- 'token.' . format ( self . operation , self . articled_short_name ))
283
- elif header .get ('alg' ) == 'HS256' and payload .get (
285
+ if audience == FIREBASE_AUDIENCE :
286
+ error_message = (
287
+ '{0} expects {1}, but was given a custom '
288
+ 'token.' . format ( self . operation , self . articled_short_name ))
289
+ elif not header . get ( 'kid' ):
290
+ if header .get ('alg' ) == 'HS256' and payload .get (
284
291
'v' ) is 0 and 'uid' in payload .get ('d' , {}):
285
292
error_message = (
286
293
'{0} expects {1}, but was given a legacy custom '
@@ -315,19 +322,64 @@ def verify(self, token, request):
315
322
'{1}' .format (self .short_name , verify_id_token_msg ))
316
323
317
324
if error_message :
318
- raise ValueError (error_message )
325
+ raise self . invalid_token_error (error_message )
319
326
320
- verified_claims = google .oauth2 .id_token .verify_token (
321
- token ,
322
- request = request ,
323
- audience = self .project_id ,
324
- certs_url = self .cert_url )
325
- verified_claims ['uid' ] = verified_claims ['sub' ]
326
- return verified_claims
327
+ try :
328
+ verified_claims = google .oauth2 .id_token .verify_token (
329
+ token ,
330
+ request = request ,
331
+ audience = self .project_id ,
332
+ certs_url = self .cert_url )
333
+ verified_claims ['uid' ] = verified_claims ['sub' ]
334
+ return verified_claims
335
+ except google .auth .exceptions .TransportError as error :
336
+ msg = 'Failed to fetch requried public key certificates: {0}' .format (error )
337
+ raise CertificateFetchError (msg , error )
338
+ except ValueError as error :
339
+ message = str (error )
340
+ if 'Token expired' in message :
341
+ raise self .expired_token_error (message , error )
342
+ raise self .invalid_token_error ('Invalid ID token: {0}' .format (message ), error )
343
+
344
+ def _decode_unverified (self , token ):
345
+ try :
346
+ header = jwt .decode_header (token )
347
+ payload = jwt .decode (token , verify = False )
348
+ return header , payload
349
+ except ValueError as error :
350
+ raise self .invalid_token_error (str (error ), error )
327
351
328
352
329
353
class TokenSignError (exceptions .UnknownError ):
330
354
"""Unexpected error while signing a Firebase custom token."""
331
355
332
356
def __init__ (self , message , cause ):
333
357
exceptions .UnknownError .__init__ (self , message , cause )
358
+
359
+
360
+ class CertificateFetchError (exceptions .UnknownError ):
361
+ """Error while retrieving public key certificates required to verify a token."""
362
+
363
+ def __init__ (self , message , cause ):
364
+ exceptions .UnknownError .__init__ (self , message , cause )
365
+
366
+
367
+ class ExpiredIdTokenError (_auth_utils .InvalidIdTokenError ):
368
+ """The provided Firebae ID token is expired."""
369
+
370
+ def __init__ (self , message , cause ):
371
+ _auth_utils .InvalidIdTokenError .__init__ (self , message , cause )
372
+
373
+
374
+ class InvalidSessionCookieError (exceptions .InvalidArgumentError ):
375
+ """The provided string is not a valid Firebase session cookie."""
376
+
377
+ def __init__ (self , message , cause = None ):
378
+ exceptions .InvalidArgumentError .__init__ (self , message , cause )
379
+
380
+
381
+ class ExpiredSessionCookieError (InvalidSessionCookieError ):
382
+ """The provided Firebase session cookie is expired."""
383
+
384
+ def __init__ (self , message , cause ):
385
+ InvalidSessionCookieError .__init__ (self , message , cause )
0 commit comments