feat(auth): wire changePassword with reauth + typed exceptions in FirebaseAuthDataSource
This commit is contained in:
parent
07eaf3c989
commit
d1e23f24ee
|
|
@ -3,11 +3,15 @@ package app.closer.data.remote
|
|||
import app.closer.BuildConfig
|
||||
import app.closer.domain.model.AuthState
|
||||
import app.closer.domain.model.GoogleSignInResult
|
||||
import app.closer.domain.repository.ChangePasswordException
|
||||
import app.closer.domain.repository.PasswordResetException
|
||||
import com.google.firebase.auth.EmailAuthProvider
|
||||
import com.google.firebase.auth.FirebaseAuth
|
||||
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException
|
||||
import com.google.firebase.auth.FirebaseAuthInvalidUserException
|
||||
import com.google.firebase.FirebaseTooManyRequestsException
|
||||
import com.google.firebase.auth.FirebaseAuthRecentLoginRequiredException
|
||||
import com.google.firebase.auth.FirebaseAuthWeakPasswordException
|
||||
import com.google.firebase.auth.GoogleAuthProvider
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -119,6 +123,60 @@ class FirebaseAuthDataSource @Inject constructor() {
|
|||
.addOnFailureListener { cont.resumeWithException(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the signed-in user's password: re-authenticates with [currentPassword] (Firebase requires
|
||||
* a recent login to change credentials), then sets [newPassword]. Firebase specifics are translated
|
||||
* into typed [ChangePasswordException]s so the UI never string-matches error messages. Firebase's own
|
||||
* server-side throttling ([FirebaseTooManyRequestsException]) guards against brute-forcing the current
|
||||
* password; we surface it as [ChangePasswordException.TooManyAttempts].
|
||||
*/
|
||||
suspend fun changePassword(currentPassword: String, newPassword: String) {
|
||||
val user = auth.currentUser ?: throw ChangePasswordException.ReauthRequired()
|
||||
val email = user.email
|
||||
val hasPasswordProvider = user.providerData.any { it.providerId == EmailAuthProvider.PROVIDER_ID }
|
||||
if (email.isNullOrBlank() || !hasPasswordProvider) throw ChangePasswordException.NoPassword()
|
||||
|
||||
// 1) Prove it's really them with the current password.
|
||||
try {
|
||||
reauthenticate(user, EmailAuthProvider.getCredential(email, currentPassword))
|
||||
} catch (e: FirebaseAuthInvalidCredentialsException) {
|
||||
throw ChangePasswordException.WrongCurrentPassword()
|
||||
} catch (e: FirebaseAuthInvalidUserException) {
|
||||
throw ChangePasswordException.WrongCurrentPassword()
|
||||
} catch (e: FirebaseTooManyRequestsException) {
|
||||
throw ChangePasswordException.TooManyAttempts()
|
||||
}
|
||||
|
||||
// 2) Set the new password.
|
||||
try {
|
||||
updatePassword(user, newPassword)
|
||||
} catch (e: FirebaseAuthWeakPasswordException) {
|
||||
throw ChangePasswordException.WeakNewPassword()
|
||||
} catch (e: FirebaseAuthRecentLoginRequiredException) {
|
||||
throw ChangePasswordException.ReauthRequired()
|
||||
} catch (e: FirebaseTooManyRequestsException) {
|
||||
throw ChangePasswordException.TooManyAttempts()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun reauthenticate(
|
||||
user: com.google.firebase.auth.FirebaseUser,
|
||||
credential: com.google.firebase.auth.AuthCredential
|
||||
): Unit = suspendCancellableCoroutine { cont ->
|
||||
user.reauthenticate(credential)
|
||||
.addOnSuccessListener { cont.resume(Unit) }
|
||||
.addOnFailureListener { cont.resumeWithException(it) }
|
||||
}
|
||||
|
||||
private suspend fun updatePassword(
|
||||
user: com.google.firebase.auth.FirebaseUser,
|
||||
newPassword: String
|
||||
): Unit = suspendCancellableCoroutine { cont ->
|
||||
user.updatePassword(newPassword)
|
||||
.addOnSuccessListener { cont.resume(Unit) }
|
||||
.addOnFailureListener { cont.resumeWithException(it) }
|
||||
}
|
||||
|
||||
suspend fun signInWithGoogle(idToken: String): GoogleSignInResult =
|
||||
suspendCancellableCoroutine { cont ->
|
||||
val credential = GoogleAuthProvider.getCredential(idToken, null)
|
||||
|
|
|
|||
Loading…
Reference in New Issue