@@ -25,6 +25,8 @@ import (
2525 "time"
2626
2727 "firebase.google.com/go/v4/internal"
28+ "golang.org/x/oauth2"
29+ "google.golang.org/api/option"
2830 "google.golang.org/api/transport"
2931)
3032
@@ -37,6 +39,7 @@ const (
3739
3840 // SDK-generated error codes
3941 idTokenRevoked = "ID_TOKEN_REVOKED"
42+ userDisabled = "USER_DISABLED"
4043 sessionCookieRevoked = "SESSION_COOKIE_REVOKED"
4144 tenantIDMismatch = "TENANT_ID_MISMATCH"
4245)
@@ -46,6 +49,10 @@ var reservedClaims = []string{
4649 "exp" , "firebase" , "iat" , "iss" , "jti" , "nbf" , "nonce" , "sub" ,
4750}
4851
52+ var emulatorToken = & oauth2.Token {
53+ AccessToken : "owner" ,
54+ }
55+
4956// Client is the interface for the Firebase auth service.
5057//
5158// 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)
113120 return nil , err
114121 }
115122
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 ... )
117132 if err != nil {
118133 return nil , err
119134 }
@@ -288,14 +303,14 @@ func (c *baseClient) withTenantID(tenantID string) *baseClient {
288303// These keys get cached up to 24 hours, and therefore the RPC overhead gets amortized
289304// over many invocations of this function.
290305//
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()`
292307// when a revocation check is needed.
293308func (c * baseClient ) VerifyIDToken (ctx context.Context , idToken string ) (* Token , error ) {
294309 return c .verifyIDToken (ctx , idToken , false )
295310}
296311
297312// 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 .
299314//
300315// Unlike `VerifyIDToken()`, this function must make an RPC call to perform the revocation check.
301316// 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
304319 return c .verifyIDToken (ctx , idToken , true )
305320}
306321
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 ) {
308323 decoded , err := c .idTokenVerifier .VerifyToken (ctx , idToken , c .isEmulator )
309324 if err != nil {
310325 return nil , err
@@ -320,21 +335,11 @@ func (c *baseClient) verifyIDToken(ctx context.Context, idToken string, checkRev
320335 }
321336 }
322337
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" )
325340 if err != nil {
326341 return nil , err
327342 }
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- }
338343 }
339344
340345 return decoded , nil
@@ -347,11 +352,18 @@ func IsTenantIDMismatch(err error) bool {
347352
348353// IsIDTokenRevoked checks if the given error was due to a revoked ID token.
349354//
350- // When IsIDTokenRevoked returns true, IsIDTokenInvalid is guranteed to return true.
355+ // When IsIDTokenRevoked returns true, IsIDTokenInvalid is guaranteed to return true.
351356func IsIDTokenRevoked (err error ) bool {
352357 return hasAuthErrorCode (err , idTokenRevoked )
353358}
354359
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+
355367// VerifySessionCookie verifies the signature and payload of the provided Firebase session cookie.
356368//
357369// 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)
371383}
372384
373385// 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 .
375387//
376388// Unlike `VerifySessionCookie()`, this function must make an RPC call to perform the revocation check.
377389// 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
380392 return c .verifySessionCookie (ctx , sessionCookie , true )
381393}
382394
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 ) {
384396 decoded , err := c .cookieVerifier .VerifyToken (ctx , sessionCookie , c .isEmulator )
385397 if err != nil {
386398 return nil , err
387399 }
388400
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" )
391403 if err != nil {
392404 return nil , err
393405 }
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- }
404406 }
405407
406408 return decoded , nil
407409}
408410
409411// IsSessionCookieRevoked checks if the given error was due to a revoked session cookie.
410412//
411- // When IsSessionCookieRevoked returns true, IsSessionCookieInvalid is guranteed to return true.
413+ // When IsSessionCookieRevoked returns true, IsSessionCookieInvalid is guaranteed to return true.
412414func IsSessionCookieRevoked (err error ) bool {
413415 return hasAuthErrorCode (err , sessionCookieRevoked )
414416}
415417
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 {
417420 user , err := c .GetUser (ctx , token .UID )
418421 if err != nil {
419- return false , err
422+ return err
420423 }
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+ }
421432
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
423444}
424445
425446func hasAuthErrorCode (err error , code string ) bool {
0 commit comments