From 3c7408e21e41407bdf5c991a68a76805eec24393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Wed, 20 Nov 2024 22:11:52 +0100 Subject: [PATCH 1/3] Implement registration with required verified email --- .env.template | 3 +- src/api/core/accounts.rs | 41 +++++++++++-- src/api/core/mod.rs | 3 + src/api/identity.rs | 59 ++++++++++++++++++- src/auth.rs | 32 ++++++++++ src/config.rs | 4 +- src/mail.rs | 22 +++++++ .../templates/email/register_verify_email.hbs | 8 +++ .../email/register_verify_email.html.hbs | 24 ++++++++ 9 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 src/static/templates/email/register_verify_email.hbs create mode 100644 src/static/templates/email/register_verify_email.html.hbs diff --git a/.env.template b/.env.template index 62ce5258..43748f48 100644 --- a/.env.template +++ b/.env.template @@ -229,7 +229,8 @@ # SIGNUPS_ALLOWED=true ## Controls if new users need to verify their email address upon registration -## Note that setting this option to true prevents logins until the email address has been verified! +## On new client versions, this will require the user to verify their email at signup time. +## On older clients, it will require the user to verify their email before they can log in. ## The welcome email will include a verification link, and login attempts will periodically ## trigger another verification email to be sent. # SIGNUPS_VERIFY=false diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 87e44529..e9a91efa 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -68,18 +68,29 @@ pub fn routes() -> Vec { #[serde(rename_all = "camelCase")] pub struct RegisterData { email: String, + kdf: Option, kdf_iterations: Option, kdf_memory: Option, kdf_parallelism: Option, + + #[serde(alias = "userSymmetricKey")] key: String, + #[serde(alias = "userAsymmetricKeys")] keys: Option, + master_password_hash: String, master_password_hint: Option, + name: Option, - token: Option, + #[allow(dead_code)] organization_user_id: Option, + #[serde(alias = "orgInviteToken")] + token: Option, + + // Used only from the register/finish endpoint + email_verification_token: Option, } #[derive(Debug, Deserialize)] @@ -122,13 +133,31 @@ async fn is_email_2fa_required(org_user_uuid: Option, conn: &mut DbConn) #[post("/accounts/register", data = "")] async fn register(data: Json, conn: DbConn) -> JsonResult { - _register(data, conn).await + _register(data, false, conn).await } -pub async fn _register(data: Json, mut conn: DbConn) -> JsonResult { - let data: RegisterData = data.into_inner(); +pub async fn _register(data: Json, email_verification: bool, mut conn: DbConn) -> JsonResult { + let mut data: RegisterData = data.into_inner(); let email = data.email.to_lowercase(); + if email_verification && data.email_verification_token.is_none() { + err!("Email verification token is required"); + } + + let email_verified = match &data.email_verification_token { + Some(token) if email_verification => { + let claims = crate::auth::decode_register_verify(token)?; + if claims.sub != data.email { + err!("Email verification token does not match email"); + } + + // During this call, we don't get the name, so extract it from the claims + data.name = Some(claims.name); + claims.verified + } + _ => false, + }; + // Check if the length of the username exceeds 50 characters (Same is Upstream Bitwarden) // This also prevents issues with very long usernames causing to large JWT's. See #2419 if let Some(ref name) = data.name { @@ -198,6 +227,10 @@ pub async fn _register(data: Json, mut conn: DbConn) -> JsonResult user.client_kdf_iter = client_kdf_iter; } + if email_verified { + user.verified_at = Some(Utc::now().naive_utc()); + } + user.client_kdf_memory = data.kdf_memory; user.client_kdf_parallelism = data.kdf_parallelism; diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 75c63c16..122bf44f 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -193,6 +193,9 @@ fn config() -> Json { feature_states.insert("key-rotation-improvements".to_string(), true); feature_states.insert("flexible-collections-v-1".to_string(), false); + feature_states.insert("email-verification".to_string(), true); + feature_states.insert("unauth-ui-refresh".to_string(), true); + Json(json!({ // Note: The clients use this version to handle backwards compatibility concerns // This means they expect a version that closely matches the Bitwarden server version diff --git a/src/api/identity.rs b/src/api/identity.rs index 445d61fd..02c8529c 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -24,7 +24,7 @@ use crate::{ }; pub fn routes() -> Vec { - routes![login, prelogin, identity_register] + routes![login, prelogin, identity_register, register_verification_email, register_finish] } #[post("/connect/token", data = "")] @@ -719,7 +719,62 @@ async fn prelogin(data: Json, conn: DbConn) -> Json { #[post("/accounts/register", data = "")] async fn identity_register(data: Json, conn: DbConn) -> JsonResult { - _register(data, conn).await + _register(data, false, conn).await +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct RegisterVerificationData { + email: String, + name: String, + // receiveMarketingEmails: bool, +} + +#[derive(rocket::Responder)] +enum RegisterVerificationResponse { + NoContent(()), + Token(Json), +} + +#[post("/accounts/register/send-verification-email", data = "")] +async fn register_verification_email( + data: Json, + mut conn: DbConn, +) -> ApiResult { + let data = data.into_inner(); + + if !CONFIG.is_signup_allowed(&data.email) { + err!("Registration not allowed or user already exists") + } + + // TODO: We might want to do some rate limiting here + // Also, test this with invites/emergency access etc + + if User::find_by_mail(&data.email, &mut conn).await.is_some() { + // TODO: Add some random delay here to prevent timing attacks? + return Ok(RegisterVerificationResponse::NoContent(())); + } + + let should_send_mail = CONFIG.mail_enabled() && CONFIG.signups_verify(); + + let token_claims = + crate::auth::generate_register_verify_claims(data.email.clone(), data.name.clone(), should_send_mail); + let token = crate::auth::encode_jwt(&token_claims); + + if should_send_mail { + mail::send_register_verify_email(&data.email, &data.name, &token).await?; + + Ok(RegisterVerificationResponse::NoContent(())) + } else { + // If email verification is not required, return the token directly + // the clients will use this token to finish the registration + Ok(RegisterVerificationResponse::Token(Json(token))) + } +} + +#[post("/accounts/register/finish", data = "")] +async fn register_finish(data: Json, conn: DbConn) -> JsonResult { + _register(data, true, conn).await } // https://github.com/bitwarden/jslib/blob/master/common/src/models/request/tokenRequest.ts diff --git a/src/auth.rs b/src/auth.rs index 809ef9fd..44b2adbe 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -31,6 +31,7 @@ static JWT_ADMIN_ISSUER: Lazy = Lazy::new(|| format!("{}|admin", CONFIG. static JWT_SEND_ISSUER: Lazy = Lazy::new(|| format!("{}|send", CONFIG.domain_origin())); static JWT_ORG_API_KEY_ISSUER: Lazy = Lazy::new(|| format!("{}|api.organization", CONFIG.domain_origin())); static JWT_FILE_DOWNLOAD_ISSUER: Lazy = Lazy::new(|| format!("{}|file_download", CONFIG.domain_origin())); +static JWT_REGISTER_VERIFY_ISSUER: Lazy = Lazy::new(|| format!("{}|register_verify", CONFIG.domain_origin())); static PRIVATE_RSA_KEY: OnceCell = OnceCell::new(); static PUBLIC_RSA_KEY: OnceCell = OnceCell::new(); @@ -141,6 +142,10 @@ pub fn decode_file_download(token: &str) -> Result { decode_jwt(token, JWT_FILE_DOWNLOAD_ISSUER.to_string()) } +pub fn decode_register_verify(token: &str) -> Result { + decode_jwt(token, JWT_REGISTER_VERIFY_ISSUER.to_string()) +} + #[derive(Debug, Serialize, Deserialize)] pub struct LoginJwtClaims { // Not before @@ -308,6 +313,33 @@ pub fn generate_file_download_claims(uuid: String, file_id: String) -> FileDownl } } +#[derive(Debug, Serialize, Deserialize)] +pub struct RegisterVerifyClaims { + // Not before + pub nbf: i64, + // Expiration time + pub exp: i64, + // Issuer + pub iss: String, + // Subject + pub sub: String, + + pub name: String, + pub verified: bool, +} + +pub fn generate_register_verify_claims(email: String, name: String, verified: bool) -> RegisterVerifyClaims { + let time_now = Utc::now(); + RegisterVerifyClaims { + nbf: time_now.timestamp(), + exp: (time_now + TimeDelta::try_minutes(30).unwrap()).timestamp(), + iss: JWT_REGISTER_VERIFY_ISSUER.to_string(), + sub: email, + name, + verified, + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct BasicJwtClaims { // Not before diff --git a/src/config.rs b/src/config.rs index e4e80927..c4a8a005 100644 --- a/src/config.rs +++ b/src/config.rs @@ -472,7 +472,8 @@ make_config! { disable_icon_download: bool, true, def, false; /// Allow new signups |> Controls whether new users can register. Users can be invited by the vaultwarden admin even if this is disabled signups_allowed: bool, true, def, true; - /// Require email verification on signups. This will prevent logins from succeeding until the address has been verified + /// Require email verification on signups. On new client versions, this will require verification at signup time. On older clients, + /// this will prevent logins from succeeding until the address has been verified signups_verify: bool, true, def, false; /// If signups require email verification, automatically re-send verification email if it hasn't been sent for a while (in seconds) signups_verify_resend_time: u64, true, def, 3_600; @@ -1353,6 +1354,7 @@ where reg!("email/protected_action", ".html"); reg!("email/pw_hint_none", ".html"); reg!("email/pw_hint_some", ".html"); + reg!("email/register_verify_email", ".html"); reg!("email/send_2fa_removed_from_org", ".html"); reg!("email/send_emergency_access_invite", ".html"); reg!("email/send_org_invite", ".html"); diff --git a/src/mail.rs b/src/mail.rs index 5ce4a079..1754400b 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -202,6 +202,28 @@ pub async fn send_verify_email(address: &str, uuid: &str) -> EmptyResult { send_email(address, &subject, body_html, body_text).await } +pub async fn send_register_verify_email(email: &str, name: &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); + let query_string = match query.query() { + None => err!("Failed to build verify URL query parameters"), + Some(query) => query, + }; + + let (subject, body_html, body_text) = get_text( + "email/register_verify_email", + json!({ + // `url.Url` would place the anchor `#` after the query parameters + "url": format!("{}/#/finish-signup/?{}", CONFIG.domain(), query_string), + "img_src": CONFIG._smtp_img_src(), + "name": name, + "email": email, + }), + )?; + + send_email(email, &subject, body_html, body_text).await +} + pub async fn send_welcome(address: &str) -> EmptyResult { let (subject, body_html, body_text) = get_text( "email/welcome", diff --git a/src/static/templates/email/register_verify_email.hbs b/src/static/templates/email/register_verify_email.hbs new file mode 100644 index 00000000..37eaab9e --- /dev/null +++ b/src/static/templates/email/register_verify_email.hbs @@ -0,0 +1,8 @@ +Verify Your Email + +Verify this email address to finish creating your account by clicking the link below. + +Verify Email Address Now: {{{url}}} + +If you did not request to verify your account, you can safely ignore this email. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/src/static/templates/email/register_verify_email.html.hbs b/src/static/templates/email/register_verify_email.html.hbs new file mode 100644 index 00000000..b3d382a0 --- /dev/null +++ b/src/static/templates/email/register_verify_email.html.hbs @@ -0,0 +1,24 @@ +Verify Your Email + +{{> email/email_header }} + + + + + + + + + + +
+ Verify this email address to finish creating your account by clicking the link below. +
+ + Verify Email Address Now + +
+ If you did not request to verify your account, you can safely ignore this email. +
+{{> email/email_footer }} \ No newline at end of file From efc82da5f66d9f99a04c9851d4198b96a5464200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Wed, 5 Feb 2025 23:46:48 +0100 Subject: [PATCH 2/3] Optional name, emergency access, and signups_allowed --- src/api/core/accounts.rs | 110 +++++++++++++++--- src/api/identity.rs | 20 ++-- src/auth.rs | 4 +- src/mail.rs | 3 +- .../templates/scss/vaultwarden.scss.hbs | 7 ++ 5 files changed, 114 insertions(+), 30 deletions(-) diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index d15648f7..e4eba00c 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -86,13 +86,15 @@ pub struct RegisterData { name: Option, - #[serde(alias = "orgInviteToken")] token: Option, #[allow(dead_code)] organization_user_id: Option, // Used only from the register/finish endpoint email_verification_token: Option, + accept_emergency_access_id: Option, + accept_emergency_access_invite_token: Option, + org_invite_token: Option, } #[derive(Debug, Deserialize)] @@ -142,23 +144,62 @@ pub async fn _register(data: Json, email_verification: bool, mut c let mut data: RegisterData = data.into_inner(); let email = data.email.to_lowercase(); - if email_verification && data.email_verification_token.is_none() { - err!("Email verification token is required"); - } + let mut email_verified = false; - let email_verified = match &data.email_verification_token { - Some(token) if email_verification => { - let claims = crate::auth::decode_register_verify(token)?; - if claims.sub != data.email { - err!("Email verification token does not match email"); + let mut pending_emergency_access = None; + + // First, validate the provided verification tokens + if email_verification { + match ( + &data.email_verification_token, + &data.accept_emergency_access_id, + &data.accept_emergency_access_invite_token, + &data.organization_user_id, + &data.org_invite_token, + ) { + // Normal user registration, when email verification is required + (Some(email_verification_token), None, None, None, None) => { + let claims = crate::auth::decode_register_verify(email_verification_token)?; + if claims.sub != data.email { + err!("Email verification token does not match email"); + } + + // During this call we don't get the name, so extract it from the claims + if claims.name.is_some() { + data.name = claims.name; + } + email_verified = claims.verified; + } + // Emergency access registration + (None, Some(accept_emergency_access_id), Some(accept_emergency_access_invite_token), None, None) => { + if !CONFIG.emergency_access_allowed() { + err!("Emergency access is not enabled.") + } + + let claims = crate::auth::decode_emergency_access_invite(accept_emergency_access_invite_token)?; + + // This can happen if the user who received the invite used a different email to signup. + // Since we do not know if this is intended, we error out here and do nothing with the invite. + if claims.email != data.email { + err!("Claim email does not match email") + } + if &claims.emer_id != accept_emergency_access_id { + err!("Claim emer_id does not match accept_emergency_access_id") + } + + pending_emergency_access = Some((accept_emergency_access_id, claims)); + email_verified = true; + } + // Org invite + (None, None, None, Some(_organization_user_id), Some(_org_invite_token)) => { + err!("Org invite") } - // During this call, we don't get the name, so extract it from the claims - data.name = Some(claims.name); - claims.verified + _ => { + err!("Registration is missing required parameters") + } } - _ => false, - }; + } // Check if the length of the username exceeds 50 characters (Same is Upstream Bitwarden) // This also prevents issues with very long usernames causing to large JWT's. See #2419 @@ -210,7 +251,10 @@ pub async fn _register(data: Json, email_verification: bool, mut c // Order is important here; the invitation check must come first // because the vaultwarden admin can invite anyone, regardless // of other signup restrictions. - if Invitation::take(&email, &mut conn).await || CONFIG.is_signup_allowed(&email) { + if Invitation::take(&email, &mut conn).await + || CONFIG.is_signup_allowed(&email) + || pending_emergency_access.is_some() + { User::new(email.clone()) } else { err!("Registration not allowed or user already exists") @@ -266,10 +310,38 @@ pub async fn _register(data: Json, email_verification: bool, mut c user.save(&mut conn).await?; - // accept any open emergency access invitations - if !CONFIG.mail_enabled() && CONFIG.emergency_access_allowed() { - for mut emergency_invite in EmergencyAccess::find_all_invited_by_grantee_email(&user.email, &mut conn).await { - emergency_invite.accept_invite(&user.uuid, &user.email, &mut conn).await.ok(); + if CONFIG.emergency_access_allowed() { + // Accept the emergency access invitation + if let Some((accept_emergency_access_id, claims)) = pending_emergency_access { + let Some(mut emergency_access) = + EmergencyAccess::find_by_uuid_and_grantee_email(accept_emergency_access_id, &data.email, &mut conn) + .await + else { + err!("Emergency access not valid.") + }; + + // get grantor user to send Accepted email + let Some(grantor_user) = User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await else { + err!("Grantor user not found.") + }; + + if grantor_user.name == claims.grantor_name && grantor_user.email == claims.grantor_email { + emergency_access.accept_invite(&user.uuid, &user.email, &mut conn).await?; + + if CONFIG.mail_enabled() { + mail::send_emergency_access_invite_accepted(&grantor_user.email, &user.email).await?; + } + } else { + err!("Emergency access invitation error.") + } + } + + // accept any open emergency access invitations + if !CONFIG.mail_enabled() { + for mut emergency_invite in EmergencyAccess::find_all_invited_by_grantee_email(&user.email, &mut conn).await + { + emergency_invite.accept_invite(&user.uuid, &user.email, &mut conn).await.ok(); + } } } diff --git a/src/api/identity.rs b/src/api/identity.rs index 03923456..86cdd471 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -721,7 +721,7 @@ async fn identity_register(data: Json, conn: DbConn) -> JsonResult #[serde(rename_all = "camelCase")] struct RegisterVerificationData { email: String, - name: String, + name: Option, // receiveMarketingEmails: bool, } @@ -742,22 +742,28 @@ async fn register_verification_email( err!("Registration not allowed or user already exists") } - // TODO: We might want to do some rate limiting here - // Also, test this with invites/emergency access etc + let should_send_mail = CONFIG.mail_enabled() && CONFIG.signups_verify(); if User::find_by_mail(&data.email, &mut conn).await.is_some() { - // TODO: Add some random delay here to prevent timing attacks? + if should_send_mail { + // There is still a timing side channel here in that the code + // paths that send mail take noticeably longer than ones that + // don't. Add a randomized sleep to mitigate this somewhat. + use rand::{rngs::SmallRng, Rng, SeedableRng}; + let mut rng = SmallRng::from_os_rng(); + let delta: i32 = 100; + let sleep_ms = (1_000 + rng.random_range(-delta..=delta)) as u64; + tokio::time::sleep(tokio::time::Duration::from_millis(sleep_ms)).await; + } return Ok(RegisterVerificationResponse::NoContent(())); } - let should_send_mail = CONFIG.mail_enabled() && CONFIG.signups_verify(); - let token_claims = crate::auth::generate_register_verify_claims(data.email.clone(), data.name.clone(), should_send_mail); let token = crate::auth::encode_jwt(&token_claims); if should_send_mail { - mail::send_register_verify_email(&data.email, &data.name, &token).await?; + mail::send_register_verify_email(&data.email, &token).await?; Ok(RegisterVerificationResponse::NoContent(())) } else { diff --git a/src/auth.rs b/src/auth.rs index d446109a..0fabd6a4 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -331,11 +331,11 @@ pub struct RegisterVerifyClaims { // Subject pub sub: String, - pub name: String, + pub name: Option, pub verified: bool, } -pub fn generate_register_verify_claims(email: String, name: String, verified: bool) -> RegisterVerifyClaims { +pub fn generate_register_verify_claims(email: String, name: Option, verified: bool) -> RegisterVerifyClaims { let time_now = Utc::now(); RegisterVerifyClaims { nbf: time_now.timestamp(), diff --git a/src/mail.rs b/src/mail.rs index 7a4deaec..015d8acb 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -201,7 +201,7 @@ pub async fn send_verify_email(address: &str, user_id: &UserId) -> EmptyResult { send_email(address, &subject, body_html, body_text).await } -pub async fn send_register_verify_email(email: &str, name: &str, token: &str) -> 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); let query_string = match query.query() { @@ -215,7 +215,6 @@ pub async fn send_register_verify_email(email: &str, name: &str, token: &str) -> // `url.Url` would place the anchor `#` after the query parameters "url": format!("{}/#/finish-signup/?{}", CONFIG.domain(), query_string), "img_src": CONFIG._smtp_img_src(), - "name": name, "email": email, }), )?; diff --git a/src/static/templates/scss/vaultwarden.scss.hbs b/src/static/templates/scss/vaultwarden.scss.hbs index 42c4d8dc..cdc1e266 100644 --- a/src/static/templates/scss/vaultwarden.scss.hbs +++ b/src/static/templates/scss/vaultwarden.scss.hbs @@ -93,12 +93,19 @@ bit-nav-logo bit-nav-item .bwi-shield { /**** END Static Vaultwarden Changes ****/ /**** START Dynamic Vaultwarden Changes ****/ {{#if signup_disabled}} +/* From web vault 2025.1.2 and onwards, the signup button is hidden + when signups are disabled as the web vault checks the /api/config endpoint. + Note that the clients tend to aggressively cache this endpoint, so it might + take a while for the change to take effect. To avoid the button appearing + when it shouldn't, we'll keep this style in place for a couple of versions */ +{{#if webver "<2025.3.0"}} /* Hide the register link on the login screen */ app-login form div + div + div + div + hr, app-login form div + div + div + div + hr + p { @extend %vw-hide; } {{/if}} +{{/if}} {{#unless mail_enabled}} /* Hide `Email` 2FA if mail is not enabled */ From 60a94740ef14a80556c37a50325389f8ed6ee260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Thu, 6 Feb 2025 20:48:09 +0100 Subject: [PATCH 3/3] Implement org invite, remove unneeded invite accept --- src/api/core/accounts.rs | 55 ++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index e4eba00c..58889f46 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -147,6 +147,7 @@ pub async fn _register(data: Json, email_verification: bool, mut c let mut email_verified = false; let mut pending_emergency_access = None; + let mut pending_org_invite = None; // First, validate the provided verification tokens if email_verification { @@ -178,8 +179,6 @@ pub async fn _register(data: Json, email_verification: bool, mut c let claims = crate::auth::decode_emergency_access_invite(accept_emergency_access_invite_token)?; - // This can happen if the user who received the invite used a different email to signup. - // Since we do not know if this is intended, we error out here and do nothing with the invite. if claims.email != data.email { err!("Claim email does not match email") } @@ -191,8 +190,19 @@ pub async fn _register(data: Json, email_verification: bool, mut c email_verified = true; } // Org invite - (None, None, None, Some(_organization_user_id), Some(_org_invite_token)) => { - err!("Org invite") + (None, None, None, Some(organization_user_id), Some(org_invite_token)) => { + let claims = decode_invite(org_invite_token)?; + + if claims.email != data.email { + err!("Claim email does not match email") + } + + if &claims.member_id != organization_user_id { + err!("Claim org_user_id does not match organization_user_id") + } + + pending_org_invite = Some((organization_user_id, claims)); + email_verified = true; } _ => { @@ -254,6 +264,7 @@ pub async fn _register(data: Json, email_verification: bool, mut c if Invitation::take(&email, &mut conn).await || CONFIG.is_signup_allowed(&email) || pending_emergency_access.is_some() + || pending_org_invite.is_some() { User::new(email.clone()) } else { @@ -310,38 +321,10 @@ pub async fn _register(data: Json, email_verification: bool, mut c user.save(&mut conn).await?; - if CONFIG.emergency_access_allowed() { - // Accept the emergency access invitation - if let Some((accept_emergency_access_id, claims)) = pending_emergency_access { - let Some(mut emergency_access) = - EmergencyAccess::find_by_uuid_and_grantee_email(accept_emergency_access_id, &data.email, &mut conn) - .await - else { - err!("Emergency access not valid.") - }; - - // get grantor user to send Accepted email - let Some(grantor_user) = User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await else { - err!("Grantor user not found.") - }; - - if grantor_user.name == claims.grantor_name && grantor_user.email == claims.grantor_email { - emergency_access.accept_invite(&user.uuid, &user.email, &mut conn).await?; - - if CONFIG.mail_enabled() { - mail::send_emergency_access_invite_accepted(&grantor_user.email, &user.email).await?; - } - } else { - err!("Emergency access invitation error.") - } - } - - // accept any open emergency access invitations - if !CONFIG.mail_enabled() { - for mut emergency_invite in EmergencyAccess::find_all_invited_by_grantee_email(&user.email, &mut conn).await - { - emergency_invite.accept_invite(&user.uuid, &user.email, &mut conn).await.ok(); - } + // accept any open emergency access invitations + if !CONFIG.mail_enabled() && CONFIG.emergency_access_allowed() { + for mut emergency_invite in EmergencyAccess::find_all_invited_by_grantee_email(&user.email, &mut conn).await { + emergency_invite.accept_invite(&user.uuid, &user.email, &mut conn).await.ok(); } }