I'm using Firebase Authentication in my iOS app and have implemented email verification during user registration. I also check for unverified emails upon sign-in and require users to verify their email before accessing the app, which is working as expected. However, I've noticed an unexpected behavior:
- User signs up with email and password but doesn't verify their email.
- User is not allowed to enter the app due to the email verification check.
- User resets password without verifying email.
- After changing the password, the user can access the app because isEmailVerified becomes true.
I expected isEmailVerified
to remain false after a password change since email verification and password change are separate processes. The user should still be required to verify their email before accessing the app.
Is this behavior expected in Firebase Authentication?
Code:
@MainActor
func signInWithEmail() async {
isLoading = true
validateEmail()
validatePassword()
if emailCredentials.email.isEmpty {
emailValidationState.email = .invalid("Email is required")
isLoading = false
return
}
if emailCredentials.password.isEmpty {
emailValidationState.password = .invalid("Password is required")
isLoading = false
return
}
guard case .valid = emailValidationState.email,
case .valid = emailValidationState.password else {
isLoading = false
return
}
do {
let result = try await authService.signInWithEmail(
email: emailCredentials.email,
password: emailCredentials.password
)
print("BBBB \(result.user.isEmailVerified)")
if !result.user.isEmailVerified {
emailVerificationState.credentials = AuthenticationModel.CreateAccountCredentials(
email: emailCredentials.email,
password: emailCredentials.password
)
emailVerificationState.email = emailCredentials.email
try await result.user.sendEmailVerification()
emailAuthAlert.message = AppConstants.Alert.Message.format(
AppConstants.Alert.Message.emailVerificationSent,
with: emailCredentials.email
)
emailAuthAlert.showAlert = true
} else {
handleSuccessfulAuthentication(for: .email)
}
} catch {
handleAuthError(error, for: .emailAuth)
}
isLoading = false
}
@MainActor
func resetPassword() async {
isLoading = true
validateResetPasswordEmail()
if resetPasswordCredentials.email.isEmpty {
resetPasswordValidationState.email = .invalid("Email is required")
isLoading = false
return
}
guard case .valid = resetPasswordValidationState.email else {
isLoading = false
return
}
do {
try await authService.resetPassword(email: resetPasswordCredentials.email)
fotPasswordAlert.message = AppConstants.Alert.Message.format(
AppConstants.Alert.Message.passwordResetSent,
with: resetPasswordCredentials.email
)
fotPasswordAlert.showAlert = true
} catch {
handleAuthError(error, for: .fotPassword)
}
isLoading = false
}
I'm using Firebase Authentication in my iOS app and have implemented email verification during user registration. I also check for unverified emails upon sign-in and require users to verify their email before accessing the app, which is working as expected. However, I've noticed an unexpected behavior:
- User signs up with email and password but doesn't verify their email.
- User is not allowed to enter the app due to the email verification check.
- User resets password without verifying email.
- After changing the password, the user can access the app because isEmailVerified becomes true.
I expected isEmailVerified
to remain false after a password change since email verification and password change are separate processes. The user should still be required to verify their email before accessing the app.
Is this behavior expected in Firebase Authentication?
Code:
@MainActor
func signInWithEmail() async {
isLoading = true
validateEmail()
validatePassword()
if emailCredentials.email.isEmpty {
emailValidationState.email = .invalid("Email is required")
isLoading = false
return
}
if emailCredentials.password.isEmpty {
emailValidationState.password = .invalid("Password is required")
isLoading = false
return
}
guard case .valid = emailValidationState.email,
case .valid = emailValidationState.password else {
isLoading = false
return
}
do {
let result = try await authService.signInWithEmail(
email: emailCredentials.email,
password: emailCredentials.password
)
print("BBBB \(result.user.isEmailVerified)")
if !result.user.isEmailVerified {
emailVerificationState.credentials = AuthenticationModel.CreateAccountCredentials(
email: emailCredentials.email,
password: emailCredentials.password
)
emailVerificationState.email = emailCredentials.email
try await result.user.sendEmailVerification()
emailAuthAlert.message = AppConstants.Alert.Message.format(
AppConstants.Alert.Message.emailVerificationSent,
with: emailCredentials.email
)
emailAuthAlert.showAlert = true
} else {
handleSuccessfulAuthentication(for: .email)
}
} catch {
handleAuthError(error, for: .emailAuth)
}
isLoading = false
}
@MainActor
func resetPassword() async {
isLoading = true
validateResetPasswordEmail()
if resetPasswordCredentials.email.isEmpty {
resetPasswordValidationState.email = .invalid("Email is required")
isLoading = false
return
}
guard case .valid = resetPasswordValidationState.email else {
isLoading = false
return
}
do {
try await authService.resetPassword(email: resetPasswordCredentials.email)
fotPasswordAlert.message = AppConstants.Alert.Message.format(
AppConstants.Alert.Message.passwordResetSent,
with: resetPasswordCredentials.email
)
fotPasswordAlert.showAlert = true
} catch {
handleAuthError(error, for: .fotPassword)
}
isLoading = false
}
Share
Improve this question
edited Feb 16 at 14:40
Tulon
asked Feb 16 at 14:34
TulonTulon
4,1266 gold badges38 silver badges64 bronze badges
3
- 1 Doesn't the user resetting their password involve them receiving an email with a password-reset link? – Frank van Puffelen Commented Feb 16 at 15:07
- Yes, Frank, I understand that, in a way, it indicates this email belongs to the same user. The problem is that I register user data in Firestore only when they verify their email or phone number. So, if a user signs up without verifying their email and then uses the "fot password" feature to sign in, they can access the app, but they won't have any corresponding record in Firestore. – Tulon Commented Feb 16 at 15:18
- That's the issue I'm trying to address. Now I'm thinking that every time I check the verification status of an email at sign-in, I'll first check if the user ID (UUID) already exists. If it doesn't, I'll create it; if it does, I'll update the related information, such as sign-in time and other metadata. You can share your thoughts on this approach if you'd like. Thanks. @FrankvanPuffelen – Tulon Commented Feb 16 at 15:18
1 Answer
Reset to default 2The emailVerified
flag in Firebase authentication indicates whether it has been verified that the user owns the email address; nothing more, nothing less. Given that, them clicking the link in a password-reset email verifies that they have access to that email address, so constitutes email verification.
So to your question: yes, it is expected that emailVerified
is set to true after the user reset their password through an email-link.
The problem you're having comes from the fact that you're trying to add your own meaning to the emailVerified
property, specifically whether you created a document for that user in Firestore.
To prevent this sort of mismatch, I recommend not using emailVerified
for this purpose. Rather, whenever you need to load the user's profile document from Firestore, first check if such a document exists, and if not: send them through the registration flow that creates the document.