diff --git a/migrations/mysql/2024-09-04-091351_use_device_type_for_mails/down.sql b/migrations/mysql/2024-09-04-091351_use_device_type_for_mails/down.sql
new file mode 100644
index 00000000..dd0394ee
--- /dev/null
+++ b/migrations/mysql/2024-09-04-091351_use_device_type_for_mails/down.sql
@@ -0,0 +1 @@
+ALTER TABLE `twofactor_incomplete` DROP COLUMN `device_type`;
diff --git a/migrations/mysql/2024-09-04-091351_use_device_type_for_mails/up.sql b/migrations/mysql/2024-09-04-091351_use_device_type_for_mails/up.sql
new file mode 100644
index 00000000..423e16c1
--- /dev/null
+++ b/migrations/mysql/2024-09-04-091351_use_device_type_for_mails/up.sql
@@ -0,0 +1 @@
+ALTER TABLE `twofactor_incomplete` ADD COLUMN `device_type` INTEGER NOT NULL;
diff --git a/migrations/postgresql/2024-09-04-091351_use_device_type_for_mails/down.sql b/migrations/postgresql/2024-09-04-091351_use_device_type_for_mails/down.sql
new file mode 100644
index 00000000..dd0394ee
--- /dev/null
+++ b/migrations/postgresql/2024-09-04-091351_use_device_type_for_mails/down.sql
@@ -0,0 +1 @@
+ALTER TABLE `twofactor_incomplete` DROP COLUMN `device_type`;
diff --git a/migrations/postgresql/2024-09-04-091351_use_device_type_for_mails/up.sql b/migrations/postgresql/2024-09-04-091351_use_device_type_for_mails/up.sql
new file mode 100644
index 00000000..423e16c1
--- /dev/null
+++ b/migrations/postgresql/2024-09-04-091351_use_device_type_for_mails/up.sql
@@ -0,0 +1 @@
+ALTER TABLE `twofactor_incomplete` ADD COLUMN `device_type` INTEGER NOT NULL;
diff --git a/migrations/sqlite/2024-09-04-091351_use_device_type_for_mails/down.sql b/migrations/sqlite/2024-09-04-091351_use_device_type_for_mails/down.sql
new file mode 100644
index 00000000..dd0394ee
--- /dev/null
+++ b/migrations/sqlite/2024-09-04-091351_use_device_type_for_mails/down.sql
@@ -0,0 +1 @@
+ALTER TABLE `twofactor_incomplete` DROP COLUMN `device_type`;
diff --git a/migrations/sqlite/2024-09-04-091351_use_device_type_for_mails/up.sql b/migrations/sqlite/2024-09-04-091351_use_device_type_for_mails/up.sql
new file mode 100644
index 00000000..423e16c1
--- /dev/null
+++ b/migrations/sqlite/2024-09-04-091351_use_device_type_for_mails/up.sql
@@ -0,0 +1 @@
+ALTER TABLE `twofactor_incomplete` ADD COLUMN `device_type` INTEGER NOT NULL;
diff --git a/src/api/core/two_factor/mod.rs b/src/api/core/two_factor/mod.rs
index 86443a5e..e3795eb8 100644
--- a/src/api/core/two_factor/mod.rs
+++ b/src/api/core/two_factor/mod.rs
@@ -269,8 +269,14 @@ pub async fn send_incomplete_2fa_notifications(pool: DbPool) {
"User {} did not complete a 2FA login within the configured time limit. IP: {}",
user.email, login.ip_address
);
- match mail::send_incomplete_2fa_login(&user.email, &login.ip_address, &login.login_time, &login.device_name)
- .await
+ match mail::send_incomplete_2fa_login(
+ &user.email,
+ &login.ip_address,
+ &login.login_time,
+ &login.device_name,
+ &DeviceType::from_i32(login.device_type).to_string(),
+ )
+ .await
{
Ok(_) => {
if let Err(e) = login.delete(&mut conn).await {
diff --git a/src/api/identity.rs b/src/api/identity.rs
index 27f3eac6..4244d68d 100644
--- a/src/api/identity.rs
+++ b/src/api/identity.rs
@@ -265,7 +265,7 @@ async fn _password_login(
let twofactor_token = twofactor_auth(&user, &data, &mut device, ip, conn).await?;
if CONFIG.mail_enabled() && new_device {
- if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device.name).await {
+ if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device).await {
error!("Error sending new device email: {:#?}", e);
if CONFIG.require_device_email() {
@@ -421,7 +421,7 @@ async fn _user_api_key_login(
if CONFIG.mail_enabled() && new_device {
let now = Utc::now().naive_utc();
- if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device.name).await {
+ if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device).await {
error!("Error sending new device email: {:#?}", e);
if CONFIG.require_device_email() {
@@ -535,7 +535,7 @@ async fn twofactor_auth(
return Ok(None);
}
- TwoFactorIncomplete::mark_incomplete(&user.uuid, &device.uuid, &device.name, ip, conn).await?;
+ TwoFactorIncomplete::mark_incomplete(&user.uuid, &device.uuid, &device.name, device.atype, ip, conn).await?;
let twofactor_ids: Vec<_> = twofactors.iter().map(|tf| tf.atype).collect();
let selected_id = data.two_factor_provider.unwrap_or(twofactor_ids[0]); // If we aren't given a two factor provider, assume the first one
diff --git a/src/db/models/two_factor_incomplete.rs b/src/db/models/two_factor_incomplete.rs
index 49f7691f..12813eb5 100644
--- a/src/db/models/two_factor_incomplete.rs
+++ b/src/db/models/two_factor_incomplete.rs
@@ -13,6 +13,7 @@ db_object! {
// must complete 2FA login before being added into the devices table.
pub device_uuid: String,
pub device_name: String,
+ pub device_type: i32,
pub login_time: NaiveDateTime,
pub ip_address: String,
}
@@ -23,6 +24,7 @@ impl TwoFactorIncomplete {
user_uuid: &str,
device_uuid: &str,
device_name: &str,
+ device_type: i32,
ip: &ClientIp,
conn: &mut DbConn,
) -> EmptyResult {
@@ -44,6 +46,7 @@ impl TwoFactorIncomplete {
twofactor_incomplete::user_uuid.eq(user_uuid),
twofactor_incomplete::device_uuid.eq(device_uuid),
twofactor_incomplete::device_name.eq(device_name),
+ twofactor_incomplete::device_type.eq(device_type),
twofactor_incomplete::login_time.eq(Utc::now().naive_utc()),
twofactor_incomplete::ip_address.eq(ip.ip.to_string()),
))
diff --git a/src/db/schemas/mysql/schema.rs b/src/db/schemas/mysql/schema.rs
index 58ec55a2..fa84ed05 100644
--- a/src/db/schemas/mysql/schema.rs
+++ b/src/db/schemas/mysql/schema.rs
@@ -169,6 +169,7 @@ table! {
user_uuid -> Text,
device_uuid -> Text,
device_name -> Text,
+ device_type -> Integer,
login_time -> Timestamp,
ip_address -> Text,
}
diff --git a/src/db/schemas/postgresql/schema.rs b/src/db/schemas/postgresql/schema.rs
index 10b5313e..d1ea4b02 100644
--- a/src/db/schemas/postgresql/schema.rs
+++ b/src/db/schemas/postgresql/schema.rs
@@ -169,6 +169,7 @@ table! {
user_uuid -> Text,
device_uuid -> Text,
device_name -> Text,
+ device_type -> Integer,
login_time -> Timestamp,
ip_address -> Text,
}
diff --git a/src/db/schemas/sqlite/schema.rs b/src/db/schemas/sqlite/schema.rs
index 10b5313e..d1ea4b02 100644
--- a/src/db/schemas/sqlite/schema.rs
+++ b/src/db/schemas/sqlite/schema.rs
@@ -169,6 +169,7 @@ table! {
user_uuid -> Text,
device_uuid -> Text,
device_name -> Text,
+ device_type -> Integer,
login_time -> Timestamp,
ip_address -> Text,
}
diff --git a/src/mail.rs b/src/mail.rs
index 53e6b31f..b33efd95 100644
--- a/src/mail.rs
+++ b/src/mail.rs
@@ -17,7 +17,7 @@ use crate::{
encode_jwt, generate_delete_claims, generate_emergency_access_invite_claims, generate_invite_claims,
generate_verify_email_claims,
},
- db::models::User,
+ db::models::{Device, DeviceType, User},
error::Error,
CONFIG,
};
@@ -442,9 +442,8 @@ pub async fn send_invite_confirmed(address: &str, org_name: &str) -> EmptyResult
send_email(address, &subject, body_html, body_text).await
}
-pub async fn send_new_device_logged_in(address: &str, ip: &str, dt: &NaiveDateTime, device: &str) -> EmptyResult {
+pub async fn send_new_device_logged_in(address: &str, ip: &str, dt: &NaiveDateTime, device: &Device) -> EmptyResult {
use crate::util::upcase_first;
- let device = upcase_first(device);
let fmt = "%A, %B %_d, %Y at %r %Z";
let (subject, body_html, body_text) = get_text(
@@ -453,7 +452,8 @@ pub async fn send_new_device_logged_in(address: &str, ip: &str, dt: &NaiveDateTi
"url": CONFIG.domain(),
"img_src": CONFIG._smtp_img_src(),
"ip": ip,
- "device": device,
+ "device_name": upcase_first(&device.name),
+ "device_type": DeviceType::from_i32(device.atype).to_string(),
"datetime": crate::util::format_naive_datetime_local(dt, fmt),
}),
)?;
@@ -461,9 +461,14 @@ pub async fn send_new_device_logged_in(address: &str, ip: &str, dt: &NaiveDateTi
send_email(address, &subject, body_html, body_text).await
}
-pub async fn send_incomplete_2fa_login(address: &str, ip: &str, dt: &NaiveDateTime, device: &str) -> EmptyResult {
+pub async fn send_incomplete_2fa_login(
+ address: &str,
+ ip: &str,
+ dt: &NaiveDateTime,
+ device_name: &str,
+ device_type: &str,
+) -> EmptyResult {
use crate::util::upcase_first;
- let device = upcase_first(device);
let fmt = "%A, %B %_d, %Y at %r %Z";
let (subject, body_html, body_text) = get_text(
@@ -472,7 +477,8 @@ pub async fn send_incomplete_2fa_login(address: &str, ip: &str, dt: &NaiveDateTi
"url": CONFIG.domain(),
"img_src": CONFIG._smtp_img_src(),
"ip": ip,
- "device": device,
+ "device_name": upcase_first(device_name),
+ "device_type": device_type,
"datetime": crate::util::format_naive_datetime_local(dt, fmt),
"time_limit": CONFIG.incomplete_2fa_time_limit(),
}),
diff --git a/src/static/templates/email/incomplete_2fa_login.hbs b/src/static/templates/email/incomplete_2fa_login.hbs
index d9ff3950..a7120141 100644
--- a/src/static/templates/email/incomplete_2fa_login.hbs
+++ b/src/static/templates/email/incomplete_2fa_login.hbs
@@ -1,10 +1,11 @@
-Incomplete Two-Step Login From {{{device}}}
+Incomplete Two-Step Login From {{{device_name}}}
Someone attempted to log into your account with the correct master password, but did not provide the correct token or action required to complete the two-step login process within {{time_limit}} minutes of the initial login attempt.
* Date: {{datetime}}
* IP Address: {{ip}}
-* Device Type: {{device}}
+* Device Name: {{device_name}}
+* Device Type: {{device_type}}
If this was not you or someone you authorized, then you should change your master password as soon as possible, as it is likely to be compromised.
{{> email/email_footer_text }}
diff --git a/src/static/templates/email/incomplete_2fa_login.html.hbs b/src/static/templates/email/incomplete_2fa_login.html.hbs
index 8bc1ce21..d388a1df 100644
--- a/src/static/templates/email/incomplete_2fa_login.html.hbs
+++ b/src/static/templates/email/incomplete_2fa_login.html.hbs
@@ -1,4 +1,4 @@
-Incomplete Two-Step Login From {{{device}}}
+Incomplete Two-Step Login From {{{device_name}}}
{{> email/email_header }}
@@ -19,7 +19,12 @@ Incomplete Two-Step Login From {{{device}}}
- Device Type: {{device}}
+ Device Name: {{device_name}}
+ |
+
+
+
+ Device Type: {{device_type}}
|
diff --git a/src/static/templates/email/new_device_logged_in.hbs b/src/static/templates/email/new_device_logged_in.hbs
index 9734dcbe..b76b1d25 100644
--- a/src/static/templates/email/new_device_logged_in.hbs
+++ b/src/static/templates/email/new_device_logged_in.hbs
@@ -1,10 +1,11 @@
-New Device Logged In From {{{device}}}
+New Device Logged In From {{{device_name}}}
Your account was just logged into from a new device.
* Date: {{datetime}}
* IP Address: {{ip}}
-* Device Type: {{device}}
+* Device Name: {{device_name}}
+* Device Type: {{device_type}}
You can deauthorize all devices that have access to your account from the web vault ( {{url}} ) under Settings > My Account > Deauthorize Sessions.
-{{> email/email_footer_text }}
\ No newline at end of file
+{{> email/email_footer_text }}
diff --git a/src/static/templates/email/new_device_logged_in.html.hbs b/src/static/templates/email/new_device_logged_in.html.hbs
index 763e7994..52365acf 100644
--- a/src/static/templates/email/new_device_logged_in.html.hbs
+++ b/src/static/templates/email/new_device_logged_in.html.hbs
@@ -1,4 +1,4 @@
-New Device Logged In From {{{device}}}
+New Device Logged In From {{{device_name}}}
{{> email/email_header }}
@@ -19,7 +19,12 @@ New Device Logged In From {{{device}}}
- Device Type: {{device}}
+ Device Name: {{device_name}}
+ |
+
+
+
+ Device Type: {{device_type}}
|