Skip to content
6 changes: 3 additions & 3 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ func (c *baseClient) verifyIDToken(ctx context.Context, idToken string, checkRev
if c.tenantID != "" && c.tenantID != decoded.Firebase.Tenant {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: fmt.Sprintf("invalid tenant id: %q", decoded.Firebase.Tenant),
Message: fmt.Sprintf("invalid tenant id: %q", decoded.Firebase.Tenant),
Ext: map[string]interface{}{
authErrorCode: tenantIDMismatch,
},
Expand Down Expand Up @@ -428,7 +428,7 @@ func (c *baseClient) checkRevokedOrDisabled(ctx context.Context, token *Token, e
if user.Disabled {
return &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: "user has been disabled",
Message: "user has been disabled",
Ext: map[string]interface{}{
authErrorCode: userDisabled,
},
Expand All @@ -438,7 +438,7 @@ func (c *baseClient) checkRevokedOrDisabled(ctx context.Context, token *Token, e
if token.IssuedAt*1000 < user.TokensValidAfterMillis {
return &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: errMessage,
Message: errMessage,
Ext: map[string]interface{}{
authErrorCode: errCode,
},
Expand Down
12 changes: 6 additions & 6 deletions auth/token_verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func (tv *tokenVerifier) verifyContent(token string, isEmulator bool) (*Token, e
if token == "" {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: fmt.Sprintf("%s must be a non-empty string", tv.shortName),
Message: fmt.Sprintf("%s must be a non-empty string", tv.shortName),
Ext: map[string]interface{}{authErrorCode: tv.invalidTokenCode},
}
}
Expand All @@ -196,7 +196,7 @@ func (tv *tokenVerifier) verifyContent(token string, isEmulator bool) (*Token, e
if err != nil {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: fmt.Sprintf(
Message: fmt.Sprintf(
"%s; see %s for details on how to retrieve a valid %s",
err.Error(), tv.docURL, tv.shortName),
Ext: map[string]interface{}{authErrorCode: tv.invalidTokenCode},
Expand All @@ -210,15 +210,15 @@ func (tv *tokenVerifier) verifyTimestamps(payload *Token) error {
if (payload.IssuedAt - clockSkewSeconds) > tv.clock.Now().Unix() {
return &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: fmt.Sprintf("%s issued at future timestamp: %d", tv.shortName, payload.IssuedAt),
Message: fmt.Sprintf("%s issued at future timestamp: %d", tv.shortName, payload.IssuedAt),
Ext: map[string]interface{}{authErrorCode: tv.invalidTokenCode},
}
}

if (payload.Expires + clockSkewSeconds) < tv.clock.Now().Unix() {
return &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: fmt.Sprintf("%s has expired at: %d", tv.shortName, payload.Expires),
Message: fmt.Sprintf("%s has expired at: %d", tv.shortName, payload.Expires),
Ext: map[string]interface{}{authErrorCode: tv.expiredTokenCode},
}
}
Expand All @@ -231,15 +231,15 @@ func (tv *tokenVerifier) verifySignature(ctx context.Context, token string) erro
if err != nil {
return &internal.FirebaseError{
ErrorCode: internal.Unknown,
String: err.Error(),
Message: err.Error(),
Ext: map[string]interface{}{authErrorCode: certificateFetchFailed},
}
}

if !tv.verifySignatureWithKeys(ctx, token, keys) {
return &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: "failed to verify token signature",
Message: "failed to verify token signature",
Ext: map[string]interface{}{authErrorCode: tv.invalidTokenCode},
}
}
Expand Down
8 changes: 4 additions & 4 deletions auth/user_mgt.go
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ func (c *baseClient) GetUserByProviderUID(ctx context.Context, providerID string
if len(getUsersResult.Users) == 0 {
return nil, &internal.FirebaseError{
ErrorCode: internal.NotFound,
String: fmt.Sprintf("cannot find user from providerID: { %s, %s }", providerID, providerUID),
Message: fmt.Sprintf("cannot find user from providerID: { %s, %s }", providerID, providerUID),
Response: nil,
Ext: map[string]interface{}{
authErrorCode: userNotFound,
Expand Down Expand Up @@ -848,7 +848,7 @@ func (c *baseClient) getUser(ctx context.Context, query *userQuery) (*UserRecord
if len(parsed.Users) == 0 {
return nil, &internal.FirebaseError{
ErrorCode: internal.NotFound,
String: fmt.Sprintf("no user exists with the %s", query.description()),
Message: fmt.Sprintf("no user exists with the %s", query.description()),
Response: resp.LowLevelResponse(),
Ext: map[string]interface{}{
authErrorCode: userNotFound,
Expand Down Expand Up @@ -1487,9 +1487,9 @@ func handleHTTPError(resp *internal.Response) error {
err.ErrorCode = authErr.code
err.Ext[authErrorCode] = authErr.authCode
if detail != "" {
err.String = fmt.Sprintf("%s: %s", authErr.message, detail)
err.Message = fmt.Sprintf("%s: %s", authErr.message, detail)
} else {
err.String = authErr.message
err.Message = authErr.message
}
}

Expand Down
2 changes: 1 addition & 1 deletion db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func handleRTDBError(resp *internal.Response) error {
}
json.Unmarshal(resp.Body, &p)
if p.Error != "" {
err.String = fmt.Sprintf("http error status: %d; reason: %s", resp.Status, p.Error)
err.Message = fmt.Sprintf("http error status: %d; reason: %s", resp.Status, p.Error)
}

return err
Expand Down
137 changes: 117 additions & 20 deletions errorutils/errorutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,138 @@ package errorutils

import (
"net/http"
)

// ErrorCode represents the platform-wide error codes that can be raised by
// Admin SDK APIs.
type ErrorCode string

const (
// InvalidArgument is a OnePlatform error code.
InvalidArgument ErrorCode = "INVALID_ARGUMENT"

// FailedPrecondition is a OnePlatform error code.
FailedPrecondition ErrorCode = "FAILED_PRECONDITION"

// OutOfRange is a OnePlatform error code.
OutOfRange ErrorCode = "OUT_OF_RANGE"

// Unauthenticated is a OnePlatform error code.
Unauthenticated ErrorCode = "UNAUTHENTICATED"

// PermissionDenied is a OnePlatform error code.
PermissionDenied ErrorCode = "PERMISSION_DENIED"

// NotFound is a OnePlatform error code.
NotFound ErrorCode = "NOT_FOUND"

// Conflict is a custom error code that represents HTTP 409 responses.
//
// OnePlatform APIs typically respond with ABORTED or ALREADY_EXISTS explicitly. But a few
// old APIs send HTTP 409 Conflict without any additional details to distinguish between the two
// cases. For these we currently use this error code. As more APIs adopt OnePlatform conventions
// this will become less important.
Conflict ErrorCode = "CONFLICT"

// Aborted is a OnePlatform error code.
Aborted ErrorCode = "ABORTED"

// AlreadyExists is a OnePlatform error code.
AlreadyExists ErrorCode = "ALREADY_EXISTS"

// ResourceExhausted is a OnePlatform error code.
ResourceExhausted ErrorCode = "RESOURCE_EXHAUSTED"

// Cancelled is a OnePlatform error code.
Cancelled ErrorCode = "CANCELLED"

// DataLoss is a OnePlatform error code.
DataLoss ErrorCode = "DATA_LOSS"

// Unknown is a OnePlatform error code.
Unknown ErrorCode = "UNKNOWN"

"firebase.google.com/go/v4/internal"
// Internal is a OnePlatform error code.
Internal ErrorCode = "INTERNAL"

// Unavailable is a OnePlatform error code.
Unavailable ErrorCode = "UNAVAILABLE"

// DeadlineExceeded is a OnePlatform error code.
DeadlineExceeded ErrorCode = "DEADLINE_EXCEEDED"
)

// FirebaseError is an error type containing an:
// - error code string.
// - error message.
// - HTTP response that caused this error, if any.
// - additional metadata about the error.
type FirebaseError struct {
ErrorCode ErrorCode
Message string
Response *http.Response
Ext map[string]interface{}
}

// Error implements the error interface.
func (fe *FirebaseError) Error() string {
return fe.Message
}

// Code returns the canonical error code associated with this error.
func (fe *FirebaseError) Code() ErrorCode {
return fe.ErrorCode
}

// HTTPResponse returns the HTTP response that caused this error, if any.
// Returns nil if the error was not caused by an HTTP response.
func (fe *FirebaseError) HTTPResponse() *http.Response {
return fe.Response
}

// Extensions returns additional metadata associated with this error.
// Returns nil if no extensions are available.
func (fe *FirebaseError) Extensions() map[string]interface{} {
return fe.Ext
}

// IsInvalidArgument checks if the given error was due to an invalid client argument.
func IsInvalidArgument(err error) bool {
return internal.HasPlatformErrorCode(err, internal.InvalidArgument)
return HasPlatformErrorCode(err, InvalidArgument)
}

// IsFailedPrecondition checks if the given error was because a request could not be executed
// in the current system state, such as deleting a non-empty directory.
func IsFailedPrecondition(err error) bool {
return internal.HasPlatformErrorCode(err, internal.FailedPrecondition)
return HasPlatformErrorCode(err, FailedPrecondition)
}

// IsOutOfRange checks if the given error due to an invalid range specified by the client.
func IsOutOfRange(err error) bool {
return internal.HasPlatformErrorCode(err, internal.OutOfRange)
return HasPlatformErrorCode(err, OutOfRange)
}

// IsUnauthenticated checks if the given error was caused by an unauthenticated request.
//
// Unauthenticated requests are due to missing, invalid, or expired OAuth token.
func IsUnauthenticated(err error) bool {
return internal.HasPlatformErrorCode(err, internal.Unauthenticated)
return HasPlatformErrorCode(err, Unauthenticated)
}

// IsPermissionDenied checks if the given error was due to a client not having suffificient
// IsPermissionDenied checks if the given error was due to a client not having sufficient
// permissions.
//
// This can happen because the OAuth token does not have the right scopes, the client doesn't have
// permission, or the API has not been enabled for the client project.
func IsPermissionDenied(err error) bool {
return internal.HasPlatformErrorCode(err, internal.PermissionDenied)
return HasPlatformErrorCode(err, PermissionDenied)
}

// IsNotFound checks if the given error was due to a specified resource being not found.
//
// This may also occur when the request is rejected by undisclosed reasons, such as whitelisting.
func IsNotFound(err error) bool {
return internal.HasPlatformErrorCode(err, internal.NotFound)
return HasPlatformErrorCode(err, NotFound)
}

// IsConflict checks if the given error was due to a concurrency conflict, such as a
Expand All @@ -66,58 +157,58 @@ func IsNotFound(err error) bool {
// This represents an HTTP 409 Conflict status code, without additional information to distinguish
// between ABORTED or ALREADY_EXISTS error conditions.
func IsConflict(err error) bool {
return internal.HasPlatformErrorCode(err, internal.Conflict)
return HasPlatformErrorCode(err, Conflict)
}

// IsAborted checks if the given error was due to a concurrency conflict, such as a
// read-modify-write conflict.
func IsAborted(err error) bool {
return internal.HasPlatformErrorCode(err, internal.Aborted)
return HasPlatformErrorCode(err, Aborted)
}

// IsAlreadyExists checks if the given error was because a resource that a client tried to create
// already exists.
func IsAlreadyExists(err error) bool {
return internal.HasPlatformErrorCode(err, internal.AlreadyExists)
return HasPlatformErrorCode(err, AlreadyExists)
}

// IsResourceExhausted checks if the given error was caused by either running out of a quota or
// reaching a rate limit.
func IsResourceExhausted(err error) bool {
return internal.HasPlatformErrorCode(err, internal.ResourceExhausted)
return HasPlatformErrorCode(err, ResourceExhausted)
}

// IsCancelled checks if the given error was due to the client cancelling a request.
func IsCancelled(err error) bool {
return internal.HasPlatformErrorCode(err, internal.Cancelled)
return HasPlatformErrorCode(err, Cancelled)
}

// IsDataLoss checks if the given error was due to an unrecoverable data loss or corruption.
//
// The client should report such errors to the end user.
func IsDataLoss(err error) bool {
return internal.HasPlatformErrorCode(err, internal.DataLoss)
return HasPlatformErrorCode(err, DataLoss)
}

// IsUnknown checks if the given error was cuased by an unknown server error.
// IsUnknown checks if the given error was caused by an unknown server error.
//
// This typically indicates a server bug.
func IsUnknown(err error) bool {
return internal.HasPlatformErrorCode(err, internal.Unknown)
return HasPlatformErrorCode(err, Unknown)
}

// IsInternal checks if the given error was due to an internal server error.
//
// This typically indicates a server bug.
func IsInternal(err error) bool {
return internal.HasPlatformErrorCode(err, internal.Internal)
return HasPlatformErrorCode(err, Internal)
}

// IsUnavailable checks if the given error was caused by an unavailable service.
//
// This typically indicates that the target service is temporarily down.
func IsUnavailable(err error) bool {
return internal.HasPlatformErrorCode(err, internal.Unavailable)
return HasPlatformErrorCode(err, Unavailable)
}

// IsDeadlineExceeded checks if the given error was due a request exceeding a deadline.
Expand All @@ -126,7 +217,7 @@ func IsUnavailable(err error) bool {
// deadline (i.e. requested deadline is not enough for the server to process the request) and the
// request did not finish within the deadline.
func IsDeadlineExceeded(err error) bool {
return internal.HasPlatformErrorCode(err, internal.DeadlineExceeded)
return HasPlatformErrorCode(err, DeadlineExceeded)
}

// HTTPResponse returns the http.Response instance that caused the given error.
Expand All @@ -136,10 +227,16 @@ func IsDeadlineExceeded(err error) bool {
// Returns a buffered copy of the original response received from the network stack. It is safe to
// read the response content from the returned http.Response.
func HTTPResponse(err error) *http.Response {
fe, ok := err.(*internal.FirebaseError)
fe, ok := err.(*FirebaseError)
if ok {
return fe.Response
}

return nil
}

// HasPlatformErrorCode checks if the given error contains a specific error code.
func HasPlatformErrorCode(err error, code ErrorCode) bool {
fe, ok := err.(*FirebaseError)
return ok && fe.ErrorCode == code
}
Loading