mirror of
https://github.com/bitwarden/android.git
synced 2026-05-10 16:45:43 -05:00
[PM-11649] Add service call to verify email to identity service and auth repository.
This commit is contained in:
@@ -9,6 +9,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterFinishRequ
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
@@ -79,4 +80,9 @@ interface UnauthenticatedIdentityApi {
|
||||
suspend fun sendVerificationEmail(
|
||||
@Body body: SendVerificationEmailRequestJson,
|
||||
): Result<JsonPrimitive?>
|
||||
|
||||
@POST("/accounts/register/verification-email-clicked")
|
||||
suspend fun verifyEmailToken(
|
||||
@Body body: VerifyEmailTokenRequestJson,
|
||||
): Result<Unit>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.x8bit.bitwarden.data.auth.datasource.network.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Request body to verify the email token.
|
||||
*
|
||||
* @param email The email being used to create the account.
|
||||
* @param emailVerificationToken The token used to verify the email.
|
||||
*/
|
||||
@Serializable
|
||||
data class VerifyEmailTokenRequestJson(
|
||||
@SerialName("email")
|
||||
val email: String,
|
||||
|
||||
@SerialName("emailVerificationToken")
|
||||
val emailVerificationToken: String?,
|
||||
)
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.x8bit.bitwarden.data.auth.datasource.network.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Models response bodies for the verification of the email token.
|
||||
*/
|
||||
@Serializable
|
||||
sealed class VerifyEmailTokenResponseJson {
|
||||
/**
|
||||
* Models a successful json response of the verify email request.
|
||||
*/
|
||||
@Serializable
|
||||
data object Success : VerifyEmailTokenResponseJson()
|
||||
|
||||
/**
|
||||
* Represents the json body of an invalid register request.
|
||||
*
|
||||
* @param validationErrors a map where each value is a list of error messages for each key.
|
||||
* The values in the array should be used for display to the user, since the keys tend to come
|
||||
* back as nonsense. (eg: empty string key)
|
||||
*/
|
||||
@Serializable
|
||||
data class Invalid(
|
||||
@SerialName("message")
|
||||
private val invalidMessage: String? = null,
|
||||
|
||||
@SerialName("Message")
|
||||
private val errorMessage: String? = null,
|
||||
|
||||
@SerialName("validationErrors")
|
||||
val validationErrors: Map<String, List<String>>?,
|
||||
) : VerifyEmailTokenResponseJson() {
|
||||
/**
|
||||
* A generic error message.
|
||||
*/
|
||||
val message: String? get() = invalidMessage ?: errorMessage
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJso
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenResponseJson
|
||||
|
||||
/**
|
||||
* Provides an API for querying identity endpoints.
|
||||
@@ -72,4 +74,11 @@ interface IdentityService {
|
||||
* Register a new account to Bitwarden using email verification flow.
|
||||
*/
|
||||
suspend fun registerFinish(body: RegisterFinishRequestJson): Result<RegisterResponseJson>
|
||||
|
||||
/**
|
||||
* Verifies that the token received by email is still valid
|
||||
*/
|
||||
suspend fun verifyEmailToken(
|
||||
body: VerifyEmailTokenRequestJson,
|
||||
): Result<VerifyEmailTokenResponseJson>
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJso
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenResponseJson
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.toBitwardenError
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.base64UrlEncode
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.executeForResult
|
||||
@@ -127,4 +129,22 @@ class IdentityServiceImpl(
|
||||
.sendVerificationEmail(body = body)
|
||||
.map { it?.content }
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
override suspend fun verifyEmailToken(
|
||||
body: VerifyEmailTokenRequestJson,
|
||||
): Result<VerifyEmailTokenResponseJson> {
|
||||
return unauthenticatedIdentityApi
|
||||
.verifyEmailToken(body = body)
|
||||
.map { VerifyEmailTokenResponseJson.Success }
|
||||
.recoverCatching { throwable ->
|
||||
val bitwardenError = throwable.toBitwardenError()
|
||||
bitwardenError
|
||||
.parseErrorBodyOrNull<VerifyEmailTokenResponseJson.Invalid>(
|
||||
codes = (400..499).toList(),
|
||||
json = json,
|
||||
)
|
||||
?: throw throwable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePinResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.VerifyEmailTokenResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.VerifyOtpResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.DuoCallbackTokenResult
|
||||
@@ -377,4 +378,12 @@ interface AuthRepository : AuthenticatorProvider, AuthRequestManager {
|
||||
name: String,
|
||||
receiveMarketingEmails: Boolean,
|
||||
): SendVerificationEmailResult
|
||||
|
||||
/**
|
||||
* Verifies token received by email.
|
||||
*/
|
||||
suspend fun verifyEmailToken(
|
||||
email: String,
|
||||
emailVerificationToken: String,
|
||||
): VerifyEmailTokenResult
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.SetPasswordRequest
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.service.AccountsService
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.service.DevicesService
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.service.HaveIBeenPwnedService
|
||||
@@ -64,6 +65,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePinResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.VerifyEmailTokenResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.VerifyOtpResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.toLoginErrorResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
|
||||
@@ -1256,6 +1258,32 @@ class AuthRepositoryImpl(
|
||||
},
|
||||
)
|
||||
|
||||
override suspend fun verifyEmailToken(
|
||||
email: String,
|
||||
emailVerificationToken: String,
|
||||
): VerifyEmailTokenResult =
|
||||
identityService
|
||||
.verifyEmailToken(
|
||||
VerifyEmailTokenRequestJson(
|
||||
email = email,
|
||||
emailVerificationToken = emailVerificationToken,
|
||||
),
|
||||
)
|
||||
.fold(
|
||||
onSuccess = {
|
||||
VerifyEmailTokenResult.Verified
|
||||
},
|
||||
onFailure = {
|
||||
// Server sends a message if the link is expired, this is the only way
|
||||
// to check if the token is expired
|
||||
if (it.message?.contains("Expired link", ignoreCase = true) == true) {
|
||||
return VerifyEmailTokenResult.LinkExpired
|
||||
} else {
|
||||
return VerifyEmailTokenResult.Error
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
private suspend fun validatePasswordAgainstPolicy(
|
||||
password: String,
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.x8bit.bitwarden.data.auth.repository.model
|
||||
|
||||
/**
|
||||
* Models result of verifying the email token.
|
||||
*/
|
||||
sealed class VerifyEmailTokenResult {
|
||||
|
||||
/**
|
||||
* Represents a successful verification of email token.
|
||||
*/
|
||||
data object Verified : VerifyEmailTokenResult()
|
||||
|
||||
/**
|
||||
* Represents an expired email verification token.
|
||||
*/
|
||||
data object LinkExpired : VerifyEmailTokenResult()
|
||||
|
||||
/**
|
||||
* There was an error verifying email token.
|
||||
*/
|
||||
data object Error : VerifyEmailTokenResult()
|
||||
}
|
||||
Reference in New Issue
Block a user