@@ -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,76 @@ 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 )
326
+
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
+ raise CertificateFetchError (str (error ), cause = error )
337
+ except ValueError as error :
338
+ if 'Token expired' in str (error ):
339
+ raise self ._expired_token_error (str (error ), cause = error )
340
+ raise self ._invalid_token_error (str (error ), cause = error )
319
341
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
342
+ def _decode_unverified ( self , token ):
343
+ try :
344
+ header = jwt . decode_header ( token )
345
+ payload = jwt . decode ( token , verify = False )
346
+ return header , payload
347
+ except ValueError as error :
348
+ raise self . _invalid_token_error ( str ( error ), cause = error )
327
349
328
350
329
351
class TokenSignError (exceptions .UnknownError ):
330
352
"""Unexpected error while signing a Firebase custom token."""
331
353
332
354
def __init__ (self , message , cause ):
333
355
exceptions .UnknownError .__init__ (self , message , cause )
356
+
357
+
358
+ class CertificateFetchError (exceptions .UnknownError ):
359
+ """Failed to fetch some public key certificates required to verify a token."""
360
+
361
+ def __init__ (self , message , cause ):
362
+ exceptions .UnknownError .__init__ (self , message , cause )
363
+
364
+
365
+ class ExpiredIdTokenError (_auth_utils .InvalidIdTokenError ):
366
+ """The provided ID token is expired."""
367
+
368
+ def __init__ (self , message , cause ):
369
+ _auth_utils .InvalidIdTokenError .__init__ (self , message , cause )
370
+
371
+
372
+ class RevokedIdTokenError (_auth_utils .InvalidIdTokenError ):
373
+ """The provided ID token has been revoked."""
374
+
375
+ def __init__ (self , message ):
376
+ _auth_utils .InvalidIdTokenError .__init__ (self , message )
377
+
378
+
379
+ class InvalidSessionCookieError (exceptions .InvalidArgumentError ):
380
+ """The provided string is not a valid Firebase session cookie."""
381
+
382
+ def __init__ (self , message , cause = None ):
383
+ exceptions .InvalidArgumentError .__init__ (self , message , cause )
384
+
385
+
386
+ class ExpiredSessionCookieError (InvalidSessionCookieError ):
387
+ """The provided session cookie is expired."""
388
+
389
+ def __init__ (self , message , cause ):
390
+ InvalidSessionCookieError .__init__ (self , message , cause )
391
+
392
+
393
+ class RevokedSessionCookieError (InvalidSessionCookieError ):
394
+ """The provided session cookie has been revoked."""
395
+
396
+ def __init__ (self , message ):
397
+ InvalidSessionCookieError .__init__ (self , message )
0 commit comments