@@ -25,6 +25,8 @@ import (
25
25
"time"
26
26
27
27
"firebase.google.com/go/v4/internal"
28
+ "golang.org/x/oauth2"
29
+ "google.golang.org/api/option"
28
30
"google.golang.org/api/transport"
29
31
)
30
32
@@ -37,6 +39,7 @@ const (
37
39
38
40
// SDK-generated error codes
39
41
idTokenRevoked = "ID_TOKEN_REVOKED"
42
+ userDisabled = "USER_DISABLED"
40
43
sessionCookieRevoked = "SESSION_COOKIE_REVOKED"
41
44
tenantIDMismatch = "TENANT_ID_MISMATCH"
42
45
)
@@ -46,6 +49,10 @@ var reservedClaims = []string{
46
49
"exp" , "firebase" , "iat" , "iss" , "jti" , "nbf" , "nonce" , "sub" ,
47
50
}
48
51
52
+ var emulatorToken = & oauth2.Token {
53
+ AccessToken : "owner" ,
54
+ }
55
+
49
56
// Client is the interface for the Firebase auth service.
50
57
//
51
58
// Client facilitates generating custom JWT tokens for Firebase clients, and verifying ID tokens issued
@@ -113,7 +120,15 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
113
120
return nil , err
114
121
}
115
122
116
- transport , _ , err := transport .NewHTTPClient (ctx , conf .Opts ... )
123
+ var opts []option.ClientOption
124
+ if isEmulator {
125
+ ts := oauth2 .StaticTokenSource (emulatorToken )
126
+ opts = append (opts , option .WithTokenSource (ts ))
127
+ } else {
128
+ opts = append (opts , conf .Opts ... )
129
+ }
130
+
131
+ transport , _ , err := transport .NewHTTPClient (ctx , opts ... )
117
132
if err != nil {
118
133
return nil , err
119
134
}
@@ -288,14 +303,14 @@ func (c *baseClient) withTenantID(tenantID string) *baseClient {
288
303
// These keys get cached up to 24 hours, and therefore the RPC overhead gets amortized
289
304
// over many invocations of this function.
290
305
//
291
- // This does not check whether or not the token has been revoked. Use `VerifyIDTokenAndCheckRevoked()`
306
+ // This does not check whether or not the token has been revoked or disabled . Use `VerifyIDTokenAndCheckRevoked()`
292
307
// when a revocation check is needed.
293
308
func (c * baseClient ) VerifyIDToken (ctx context.Context , idToken string ) (* Token , error ) {
294
309
return c .verifyIDToken (ctx , idToken , false )
295
310
}
296
311
297
312
// VerifyIDTokenAndCheckRevoked verifies the provided ID token, and additionally checks that the
298
- // token has not been revoked.
313
+ // token has not been revoked or disabled .
299
314
//
300
315
// Unlike `VerifyIDToken()`, this function must make an RPC call to perform the revocation check.
301
316
// Developers are advised to take this additional overhead into consideration when including this
@@ -304,7 +319,7 @@ func (c *baseClient) VerifyIDTokenAndCheckRevoked(ctx context.Context, idToken s
304
319
return c .verifyIDToken (ctx , idToken , true )
305
320
}
306
321
307
- func (c * baseClient ) verifyIDToken (ctx context.Context , idToken string , checkRevoked bool ) (* Token , error ) {
322
+ func (c * baseClient ) verifyIDToken (ctx context.Context , idToken string , checkRevokedOrDisabled bool ) (* Token , error ) {
308
323
decoded , err := c .idTokenVerifier .VerifyToken (ctx , idToken , c .isEmulator )
309
324
if err != nil {
310
325
return nil , err
@@ -320,21 +335,11 @@ func (c *baseClient) verifyIDToken(ctx context.Context, idToken string, checkRev
320
335
}
321
336
}
322
337
323
- if c .isEmulator || checkRevoked {
324
- revoked , err : = c .checkRevoked (ctx , decoded )
338
+ if c .isEmulator || checkRevokedOrDisabled {
339
+ err = c .checkRevokedOrDisabled (ctx , decoded , idTokenRevoked , "ID token has been revoked" )
325
340
if err != nil {
326
341
return nil , err
327
342
}
328
-
329
- if revoked {
330
- return nil , & internal.FirebaseError {
331
- ErrorCode : internal .InvalidArgument ,
332
- String : "ID token has been revoked" ,
333
- Ext : map [string ]interface {}{
334
- authErrorCode : idTokenRevoked ,
335
- },
336
- }
337
- }
338
343
}
339
344
340
345
return decoded , nil
@@ -347,11 +352,18 @@ func IsTenantIDMismatch(err error) bool {
347
352
348
353
// IsIDTokenRevoked checks if the given error was due to a revoked ID token.
349
354
//
350
- // When IsIDTokenRevoked returns true, IsIDTokenInvalid is guranteed to return true.
355
+ // When IsIDTokenRevoked returns true, IsIDTokenInvalid is guaranteed to return true.
351
356
func IsIDTokenRevoked (err error ) bool {
352
357
return hasAuthErrorCode (err , idTokenRevoked )
353
358
}
354
359
360
+ // IsUserDisabled checks if the given error was due to a disabled ID token
361
+ //
362
+ // When IsUserDisabled returns true, IsIDTokenInvalid is guaranteed to return true.
363
+ func IsUserDisabled (err error ) bool {
364
+ return hasAuthErrorCode (err , userDisabled )
365
+ }
366
+
355
367
// VerifySessionCookie verifies the signature and payload of the provided Firebase session cookie.
356
368
//
357
369
// VerifySessionCookie accepts a signed JWT token string, and verifies that it is current, issued for the
@@ -371,7 +383,7 @@ func (c *Client) VerifySessionCookie(ctx context.Context, sessionCookie string)
371
383
}
372
384
373
385
// VerifySessionCookieAndCheckRevoked verifies the provided session cookie, and additionally checks that the
374
- // cookie has not been revoked.
386
+ // cookie has not been revoked and the user has not been disabled .
375
387
//
376
388
// Unlike `VerifySessionCookie()`, this function must make an RPC call to perform the revocation check.
377
389
// Developers are advised to take this additional overhead into consideration when including this
@@ -380,46 +392,55 @@ func (c *Client) VerifySessionCookieAndCheckRevoked(ctx context.Context, session
380
392
return c .verifySessionCookie (ctx , sessionCookie , true )
381
393
}
382
394
383
- func (c * Client ) verifySessionCookie (ctx context.Context , sessionCookie string , checkRevoked bool ) (* Token , error ) {
395
+ func (c * Client ) verifySessionCookie (ctx context.Context , sessionCookie string , checkRevokedOrDisabled bool ) (* Token , error ) {
384
396
decoded , err := c .cookieVerifier .VerifyToken (ctx , sessionCookie , c .isEmulator )
385
397
if err != nil {
386
398
return nil , err
387
399
}
388
400
389
- if c .isEmulator || checkRevoked {
390
- revoked , err := c .checkRevoked (ctx , decoded )
401
+ if c .isEmulator || checkRevokedOrDisabled {
402
+ err := c .checkRevokedOrDisabled (ctx , decoded , sessionCookieRevoked , "session cookie has been revoked" )
391
403
if err != nil {
392
404
return nil , err
393
405
}
394
-
395
- if revoked {
396
- return nil , & internal.FirebaseError {
397
- ErrorCode : internal .InvalidArgument ,
398
- String : "session cookie has been revoked" ,
399
- Ext : map [string ]interface {}{
400
- authErrorCode : sessionCookieRevoked ,
401
- },
402
- }
403
- }
404
406
}
405
407
406
408
return decoded , nil
407
409
}
408
410
409
411
// IsSessionCookieRevoked checks if the given error was due to a revoked session cookie.
410
412
//
411
- // When IsSessionCookieRevoked returns true, IsSessionCookieInvalid is guranteed to return true.
413
+ // When IsSessionCookieRevoked returns true, IsSessionCookieInvalid is guaranteed to return true.
412
414
func IsSessionCookieRevoked (err error ) bool {
413
415
return hasAuthErrorCode (err , sessionCookieRevoked )
414
416
}
415
417
416
- func (c * baseClient ) checkRevoked (ctx context.Context , token * Token ) (bool , error ) {
418
+ // checkRevokedOrDisabled checks whether the input token has been revoked or disabled.
419
+ func (c * baseClient ) checkRevokedOrDisabled (ctx context.Context , token * Token , errCode string , errMessage string ) error {
417
420
user , err := c .GetUser (ctx , token .UID )
418
421
if err != nil {
419
- return false , err
422
+ return err
420
423
}
424
+ if user .Disabled {
425
+ return & internal.FirebaseError {
426
+ ErrorCode : internal .InvalidArgument ,
427
+ String : "user has been disabled" ,
428
+ Ext : map [string ]interface {}{
429
+ authErrorCode : userDisabled ,
430
+ },
431
+ }
421
432
422
- return token .IssuedAt * 1000 < user .TokensValidAfterMillis , nil
433
+ }
434
+ if token .IssuedAt * 1000 < user .TokensValidAfterMillis {
435
+ return & internal.FirebaseError {
436
+ ErrorCode : internal .InvalidArgument ,
437
+ String : errMessage ,
438
+ Ext : map [string ]interface {}{
439
+ authErrorCode : errCode ,
440
+ },
441
+ }
442
+ }
443
+ return nil
423
444
}
424
445
425
446
func hasAuthErrorCode (err error , code string ) bool {
0 commit comments