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.BuildConfig
|
||||||
import app.closer.domain.model.AuthState
|
import app.closer.domain.model.AuthState
|
||||||
import app.closer.domain.model.GoogleSignInResult
|
import app.closer.domain.model.GoogleSignInResult
|
||||||
|
import app.closer.domain.repository.ChangePasswordException
|
||||||
import app.closer.domain.repository.PasswordResetException
|
import app.closer.domain.repository.PasswordResetException
|
||||||
import com.google.firebase.auth.EmailAuthProvider
|
import com.google.firebase.auth.EmailAuthProvider
|
||||||
import com.google.firebase.auth.FirebaseAuth
|
import com.google.firebase.auth.FirebaseAuth
|
||||||
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException
|
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException
|
||||||
import com.google.firebase.auth.FirebaseAuthInvalidUserException
|
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 com.google.firebase.auth.GoogleAuthProvider
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
@ -119,6 +123,60 @@ class FirebaseAuthDataSource @Inject constructor() {
|
||||||
.addOnFailureListener { cont.resumeWithException(it) }
|
.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 =
|
suspend fun signInWithGoogle(idToken: String): GoogleSignInResult =
|
||||||
suspendCancellableCoroutine { cont ->
|
suspendCancellableCoroutine { cont ->
|
||||||
val credential = GoogleAuthProvider.getCredential(idToken, null)
|
val credential = GoogleAuthProvider.getCredential(idToken, null)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue