diff --git a/src/api/identity.rs b/src/api/identity.rs index b61258cf..855a0fdc 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -46,6 +46,7 @@ pub fn routes() -> Vec { post_prelogin, prelogin_password, register_verification_email, + register_verification_email_clicked, register_finish, prevalidate, authorize, @@ -1034,6 +1035,13 @@ struct RegisterVerificationData { // receiveMarketingEmails: bool, } +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct RegisterVerificationEmailClickedData { + email: String, + email_verification_token: String, +} + #[derive(rocket::Responder)] enum RegisterVerificationResponse { #[response(status = 204)] @@ -1096,6 +1104,25 @@ async fn register_verification_email( } } +#[post("/accounts/register/verification-email-clicked", data = "")] +async fn register_verification_email_clicked( + data: Json, + conn: DbConn, +) -> EmptyResult { + let data = data.into_inner(); + let token_valid = auth::decode_register_verify(&data.email_verification_token) + .is_ok_and(|claims| claims.verified && claims.sub.eq_ignore_ascii_case(&data.email)); + let user_exists = User::find_by_mail(&data.email, &conn) + .await + .is_some_and(|user| !user.password_hash.is_empty() || user.private_key.is_some()); + + if !token_valid || user_exists { + err!("Expired link. Please restart registration or try logging in. You may already have an account") + } + + Ok(()) +} + #[post("/accounts/register/finish", data = "")] async fn register_finish(data: Json, ip: ClientIp, conn: DbConn) -> JsonResult { crate::ratelimit::check_limit_unauthenticated(&ip.ip)?; diff --git a/src/mail.rs b/src/mail.rs index 2efb8d70..d63357c6 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -206,7 +206,7 @@ pub async fn send_verify_email(address: &str, user_id: &UserId) -> EmptyResult { pub async fn send_register_verify_email(email: &str, token: &str) -> EmptyResult { let mut query = url::Url::parse("https://query.builder").unwrap(); - query.query_pairs_mut().append_pair("email", email).append_pair("token", token); + query.query_pairs_mut().append_pair("email", email).append_pair("token", token).append_pair("fromEmail", "true"); let Some(query_string) = query.query() else { err!("Failed to build verify URL query parameters") };