Skip to content

Commit

Permalink
Merge branch 'dev' into fix/enableSupportForEU
Browse files Browse the repository at this point in the history
  • Loading branch information
hiranya911 committed May 14, 2021
2 parents 9c436d4 + cdb46be commit f8b195e
Show file tree
Hide file tree
Showing 17 changed files with 1,496 additions and 175 deletions.
113 changes: 63 additions & 50 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
}
}

idTokenVerifier, err := newIDTokenVerifier(ctx, conf.ProjectID, isEmulator)
idTokenVerifier, err := newIDTokenVerifier(ctx, conf.ProjectID)
if err != nil {
return nil, err
}

cookieVerifier, err := newSessionCookieVerifier(ctx, conf.ProjectID, isEmulator)
cookieVerifier, err := newSessionCookieVerifier(ctx, conf.ProjectID)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -144,6 +144,7 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
cookieVerifier: cookieVerifier,
signer: signer,
clock: internal.SystemClock,
isEmulator: isEmulator,
}
return &Client{
baseClient: base,
Expand Down Expand Up @@ -265,6 +266,7 @@ type baseClient struct {
cookieVerifier *tokenVerifier
signer cryptoSigner
clock internal.Clock
isEmulator bool
}

func (c *baseClient) withTenantID(tenantID string) *baseClient {
Expand All @@ -281,63 +283,68 @@ func (c *baseClient) withTenantID(tenantID string) *baseClient {
// https://firebase.google.com/docs/auth/admin/verify-id-tokens#retrieve_id_tokens_on_clients for
// more details on how to obtain an ID token in a client app.
//
// This function does not make any RPC calls most of the time. The only time it makes an RPC call
// is when Google public keys need to be refreshed. These keys get cached up to 24 hours, and
// therefore the RPC overhead gets amortized over many invocations of this function.
// In non-emulator mode, this function does not make any RPC calls most of the time.
// The only time it makes an RPC call is when Google public keys need to be refreshed.
// These keys get cached up to 24 hours, and therefore the RPC overhead gets amortized
// over many invocations of this function.
//
// This does not check whether or not the token has been revoked. Use `VerifyIDTokenAndCheckRevoked()`
// when a revocation check is needed.
func (c *baseClient) VerifyIDToken(ctx context.Context, idToken string) (*Token, error) {
decoded, err := c.idTokenVerifier.VerifyToken(ctx, idToken)
if err == nil && c.tenantID != "" && c.tenantID != decoded.Firebase.Tenant {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: fmt.Sprintf("invalid tenant id: %q", decoded.Firebase.Tenant),
Ext: map[string]interface{}{
authErrorCode: tenantIDMismatch,
},
}
}

return decoded, err
}

// IsTenantIDMismatch checks if the given error was due to a mismatched tenant ID in a JWT.
func IsTenantIDMismatch(err error) bool {
return hasAuthErrorCode(err, tenantIDMismatch)
return c.verifyIDToken(ctx, idToken, false)
}

// VerifyIDTokenAndCheckRevoked verifies the provided ID token, and additionally checks that the
// token has not been revoked.
//
// This function uses `VerifyIDToken()` internally to verify the ID token JWT. However, unlike
// `VerifyIDToken()` this function must make an RPC call to perform the revocation check.
// Unlike `VerifyIDToken()`, this function must make an RPC call to perform the revocation check.
// Developers are advised to take this additional overhead into consideration when including this
// function in an authorization flow that gets executed often.
func (c *baseClient) VerifyIDTokenAndCheckRevoked(ctx context.Context, idToken string) (*Token, error) {
decoded, err := c.VerifyIDToken(ctx, idToken)
if err != nil {
return nil, err
}
return c.verifyIDToken(ctx, idToken, true)
}

revoked, err := c.checkRevoked(ctx, decoded)
func (c *baseClient) verifyIDToken(ctx context.Context, idToken string, checkRevoked bool) (*Token, error) {
decoded, err := c.idTokenVerifier.VerifyToken(ctx, idToken, c.isEmulator)
if err != nil {
return nil, err
}

if revoked {
if c.tenantID != "" && c.tenantID != decoded.Firebase.Tenant {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: "ID token has been revoked",
String: fmt.Sprintf("invalid tenant id: %q", decoded.Firebase.Tenant),
Ext: map[string]interface{}{
authErrorCode: idTokenRevoked,
authErrorCode: tenantIDMismatch,
},
}
}

if c.isEmulator || checkRevoked {
revoked, err := c.checkRevoked(ctx, decoded)
if err != nil {
return nil, err
}

if revoked {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: "ID token has been revoked",
Ext: map[string]interface{}{
authErrorCode: idTokenRevoked,
},
}
}
}

return decoded, nil
}

// IsTenantIDMismatch checks if the given error was due to a mismatched tenant ID in a JWT.
func IsTenantIDMismatch(err error) bool {
return hasAuthErrorCode(err, tenantIDMismatch)
}

// IsIDTokenRevoked checks if the given error was due to a revoked ID token.
//
// When IsIDTokenRevoked returns true, IsIDTokenInvalid is guranteed to return true.
Expand All @@ -352,41 +359,47 @@ func IsIDTokenRevoked(err error) bool {
// decoded claims in the input JWT. See https://firebase.google.com/docs/auth/admin/manage-cookies for more details on
// how to obtain a session cookie.
//
// This function does not make any RPC calls most of the time. The only time it makes an RPC call
// is when Google public keys need to be refreshed. These keys get cached up to 24 hours, and
// therefore the RPC overhead gets amortized over many invocations of this function.
// In non-emulator mode, this function does not make any RPC calls most of the time.
// The only time it makes an RPC call is when Google public keys need to be refreshed.
// These keys get cached up to 24 hours, and therefore the RPC overhead gets amortized
// over many invocations of this function.
//
// This does not check whether or not the cookie has been revoked. Use `VerifySessionCookieAndCheckRevoked()`
// when a revocation check is needed.
func (c *Client) VerifySessionCookie(ctx context.Context, sessionCookie string) (*Token, error) {
return c.cookieVerifier.VerifyToken(ctx, sessionCookie)
return c.verifySessionCookie(ctx, sessionCookie, false)
}

// VerifySessionCookieAndCheckRevoked verifies the provided session cookie, and additionally checks that the
// cookie has not been revoked.
//
// This function uses `VerifySessionCookie()` internally to verify the cookie JWT. However, unlike
// `VerifySessionCookie()` this function must make an RPC call to perform the revocation check.
// Unlike `VerifySessionCookie()`, this function must make an RPC call to perform the revocation check.
// Developers are advised to take this additional overhead into consideration when including this
// function in an authorization flow that gets executed often.
func (c *Client) VerifySessionCookieAndCheckRevoked(ctx context.Context, sessionCookie string) (*Token, error) {
decoded, err := c.VerifySessionCookie(ctx, sessionCookie)
if err != nil {
return nil, err
}
return c.verifySessionCookie(ctx, sessionCookie, true)
}

revoked, err := c.checkRevoked(ctx, decoded)
func (c *Client) verifySessionCookie(ctx context.Context, sessionCookie string, checkRevoked bool) (*Token, error) {
decoded, err := c.cookieVerifier.VerifyToken(ctx, sessionCookie, c.isEmulator)
if err != nil {
return nil, err
}

if revoked {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: "session cookie has been revoked",
Ext: map[string]interface{}{
authErrorCode: sessionCookieRevoked,
},
if c.isEmulator || checkRevoked {
revoked, err := c.checkRevoked(ctx, decoded)
if err != nil {
return nil, err
}

if revoked {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: "session cookie has been revoked",
Ext: map[string]interface{}{
authErrorCode: sessionCookieRevoked,
},
}
}
}

Expand Down

0 comments on commit f8b195e

Please sign in to comment.