Firebase Authentication in SwiftUI
Handle Firebase Authentication Errors
Learn how to handle Firebase Auth Errors when working with Firebase Authentication
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.
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.
Scenario 2
The user already signed-in using a provider, and then you try to link accounts using another providers (which is already linked).
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:
- Declare
authLinkErrors
array that contains the common errors to check for inauthLink(credentials:)
catch block. - Inside the catch block of
authLink(credentials:)
, check the error code against the common errors defined inauthLinkErrors
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.
Resources
“ Everyone has something to learn. Everyone has something to teach.” ~ Paul Hudson