Firebase Authentication in SwiftUI

Handle Firebase Authentication Errors

Learn how to handle Firebase Auth Errors when working with Firebase Authentication

Marwa Diab
5 min readDec 3, 2023
Photo by Jexo on Unsplash

In the previous parts of this series, we implemented

Firebase Auth Errors

If the completion callback on Authentication methods receives an NSError argument that is not nil, an error has occurred. To dispatch to appropriate error-handling logic in your production code, check the error code against common errors and method specific errors listed below.

When investigating or logging errors, review the userInfo dictionary. FIRAuthErrorNameKey contains a cross-platform error name string that can be used for identifying the error. NSLocalizedDescriptionKey contains a description of the error. This description is meant for the developer, not the user. NSUnderlyingErrorKey contains the underlying error that caused the error in question, if an underlying error is present. ~ Firebase Documentation

In order to access Firebase Auth Errors, Firebase provides AuthErrorCode.Code enum that contains all the available auth errors.
To get error code instance use init(rawValue:), as follows:

if let error = error as NSError? {
let code = AuthErrorCode.Code(rawValue: error.code)
}

As you can see in the following screenshot 👇🏻, you can access all available error codes.

Screenshot of available AuthErrorCode.Code enum options.
AuthErrorCode.Code instance.

Let’s look at some of the errors that needs to be handled when you have anonymous and multiple providers authentication.

Scenario 1

The user signed-in and out using one of the providers, then the user skipped the login process (i.e. anonymously authenticated), in this case two Firebase auth users will be created, one user with the signed-in provider, and the other user is anonymous.
The user can also be signed-in from a device, and skipped login on another device.

Authenticated firebase users, anonymous user, and Apple provider user.
Authenticated Firebase users (Anonymous and Apple).

Scenario 2

The user already signed-in using a provider, and then you try to link accounts using another providers (which is already linked).

Authenticated firebase user with both Google and Apple providers.
Authenticated Firebase user with Google and Apple providers.

In this tutorial series, Scenario 2 was already handled, and so it won’t happen, but it could happen if you have the option of linking different providers in your app settings, so in order to test this scenario, comment the code inside Sign Out button and just leave showLoginSheet = true, so that you can present LoginView.

Button {
// if authManager.authState != .signedIn {
showLoginSheet = true
// } else {
// signOut()
// }
}

Solution

In authenticateUser(credentials:) function inside AuthManager we checked if the current firebase user is not null, then link account, otherwise sign-in (as explained in Part 1: Anonymous Authentication).

In scenario 1, if the user is anonymously authenticated and you are trying to link the anonymous account with a credential that is already in use (if you’re trying to link with the same provider) or the email address is used by another account (if you are trying to link with a different provider), so link(with:) will throw one of these errors:

  • credentialAlreadyInUse: Indicates an attempt to link with a credential that has already been linked with a different Firebase account.
  • emailAlreadyInUse: Indicates the email asserted by the credential is already in use by an existing account.

In scenario 2, if the user is authenticated with Google and/or Apple, and you are trying to link the account with the same provider credentials, so link(with:) will throw the following error:

  • providerAlreadyLinked: Indicates an attempt to link a provider to which the account is already linked.

In these cases, switch to signIn(with:) instead, as follows:

  1. Declare authLinkErrors array that contains the common errors to check for in authLink(credentials:) catch block.
  2. Inside the catch block of authLink(credentials:), check the error code against the common errors defined in authLinkErrors array.
// 1.
private let authLinkErrors: [AuthErrorCode.Code] = [
.emailAlreadyInUse,
.credentialAlreadyInUse,
.providerAlreadyLinked
]
// 2.
if let error = error as NSError? {
if let code = AuthErrorCode.Code(rawValue: error.code),
authLinkErrors.contains(code) {
return try await self.authSignIn(credentials: credentials)
}

If you run this code with Google provider, it will work just fine, but with Apple provider it will throw missingOrInvalidNonce error while trying to sign-in, to solve this, retrieve the updated credentials from the error object and call authSignIn(credentials:) using the updated credentials, as follows:

// Get the updated AppleID credential before calling authSignIn(credentials:)
let appleCredentials =
credentials.provider == "apple.com"
? error.userInfo[AuthErrorUserInfoUpdatedCredentialKey] as? AuthCredential
: nil

return try await self.authSignIn(credentials: appleCredentials ?? credentials)

Be aware that now you don’t have any reference to the anonymous account anymore, if you have associated data (e.g. Firestore) that uses the data from firebase auth user, it’s recommended to migrate or merge data between accounts.

--

--