Skip to content

Commit

Permalink
register missing push devices at login
Browse files Browse the repository at this point in the history
save the push token of new device even if push notifications are not
enabled and provide a way to register the push device at login

unregister device if there already is a push token saved unless the
new token has already been registered.

also the `unregister_push_device` function used the wrong argument
cf. https://github.com/bitwarden/server/blob/08d380900b540f8d1a734c7abccaf80e59a91ced/src/Core/Services/Implementations/RelayPushRegistrationService.cs#L43
  • Loading branch information
stefan0xC committed Jan 28, 2024
1 parent edf7484 commit 6f28af7
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 21 deletions.
2 changes: 1 addition & 1 deletion src/api/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ async fn deauth_user(uuid: &str, _token: AdminToken, mut conn: DbConn, nt: Notif

if CONFIG.push_enabled() {
for device in Device::find_push_devices_by_user(&user.uuid, &mut conn).await {
match unregister_push_device(device.uuid).await {
match unregister_push_device(device.push_uuid).await {
Ok(r) => r,
Err(e) => error!("Unable to unregister devices from Bitwarden server: {}", e),
};
Expand Down
29 changes: 18 additions & 11 deletions src/api/core/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -952,26 +952,33 @@ async fn post_device_token(uuid: &str, data: JsonUpcase<PushToken>, headers: Hea

#[put("/devices/identifier/<uuid>/token", data = "<data>")]
async fn put_device_token(uuid: &str, data: JsonUpcase<PushToken>, headers: Headers, mut conn: DbConn) -> EmptyResult {
if !CONFIG.push_enabled() {
return Ok(());
}

let data = data.into_inner().data;
let token = data.PushToken;

let mut device = match Device::find_by_uuid_and_user(&headers.device.uuid, &headers.user.uuid, &mut conn).await {
Some(device) => device,
None => err!(format!("Error: device {uuid} should be present before a token can be assigned")),
};
device.push_token = Some(token);
if device.push_uuid.is_none() {
device.push_uuid = Some(uuid::Uuid::new_v4().to_string());

// if the device already has been registered
if device.is_registered() {
// check if the new token is the same as the registered token
if device.push_token.is_some() && device.push_token.unwrap() == token.clone() {
debug!("Device {} is already registered and token is the same", uuid);
return Ok(());
} else {
// Try to unregister already registered device
let _ = unregister_push_device(device.push_uuid).await;
}
// clear the push_uuid
device.push_uuid = None;
}
device.push_token = Some(token);
if let Err(e) = device.save(&mut conn).await {
err!(format!("An error occurred while trying to save the device push token: {e}"));
}
if let Err(e) = register_push_device(headers.user.uuid, device).await {
err!(format!("An error occurred while proceeding registration of a device: {e}"));
}

register_push_device(&mut device, &mut conn).await?;

Ok(())
}
Expand All @@ -988,7 +995,7 @@ async fn put_clear_device_token(uuid: &str, mut conn: DbConn) -> EmptyResult {

if let Some(device) = Device::find_by_uuid(uuid, &mut conn).await {
Device::clear_push_token_by_uuid(uuid, &mut conn).await?;
unregister_push_device(device.uuid).await?;
unregister_push_device(device.push_uuid).await?;
}

Ok(())
Expand Down
4 changes: 4 additions & 0 deletions src/api/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::{
log_user_event,
two_factor::{authenticator, duo, email, enforce_2fa_policy, webauthn, yubikey},
},
push::register_push_device,
ApiResult, EmptyResult, JsonResult, JsonUpcase,
},
auth::{generate_organization_api_key_login_claims, ClientHeaders, ClientIp},
Expand Down Expand Up @@ -266,6 +267,9 @@ async fn _password_login(
}
}

// register push device
register_push_device(&mut device, conn).await?;

// Common
// ---
// Disabled this variable, it was used to generate the JWT
Expand Down
37 changes: 28 additions & 9 deletions src/api/push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,45 +76,64 @@ async fn get_auth_push_token() -> ApiResult<String> {
Ok(push_token.access_token.clone())
}

pub async fn register_push_device(user_uuid: String, device: Device) -> EmptyResult {
if !CONFIG.push_enabled() {
pub async fn register_push_device(device: &mut Device, conn: &mut crate::db::DbConn) -> EmptyResult {
if !CONFIG.push_enabled() || !device.is_push_device() || device.is_registered() {
return Ok(());
}
let auth_push_token = get_auth_push_token().await?;

if device.push_token.is_none() {
warn!("Skipping the registration of the device {} because the push_token field is empty.", device.uuid);
warn!("To get rid of this message you need to clear the app data and reconnect the device.");
return Ok(());
}

debug!("Registering Device {}", device.uuid);

// generate a random push_uuid so we know the device is registered
device.push_uuid = Some(uuid::Uuid::new_v4().to_string());

//Needed to register a device for push to bitwarden :
let data = json!({
"userId": user_uuid,
"userId": device.user_uuid,
"deviceId": device.push_uuid,
"identifier": device.uuid,
"type": device.atype,
"pushToken": device.push_token
});

let auth_push_token = get_auth_push_token().await?;
let auth_header = format!("Bearer {}", &auth_push_token);

get_reqwest_client()
if let Err(e) = get_reqwest_client()
.post(CONFIG.push_relay_uri() + "/push/register")
.header(CONTENT_TYPE, "application/json")
.header(ACCEPT, "application/json")
.header(AUTHORIZATION, auth_header)
.json(&data)
.send()
.await?
.error_for_status()?;
.error_for_status()
{
err!(format!("An error occured while proceeding registration of a device: {e}"));
}

if let Err(e) = device.save(conn).await {
err!(format!("An error occured while trying to save the (registered) device push uuid: {e}"));
}

Ok(())
}

pub async fn unregister_push_device(uuid: String) -> EmptyResult {
if !CONFIG.push_enabled() {
pub async fn unregister_push_device(push_uuid: Option<String>) -> EmptyResult {
if !CONFIG.push_enabled() || push_uuid.is_none() {
return Ok(());
}
let auth_push_token = get_auth_push_token().await?;

let auth_header = format!("Bearer {}", &auth_push_token);

match get_reqwest_client()
.delete(CONFIG.push_relay_uri() + "/push/" + &uuid)
.delete(CONFIG.push_relay_uri() + "/push/" + &push_uuid.unwrap())
.header(AUTHORIZATION, auth_header)
.send()
.await
Expand Down
9 changes: 9 additions & 0 deletions src/db/models/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ impl Device {

(encode_jwt(&claims), DEFAULT_VALIDITY.num_seconds())
}

pub fn is_push_device(&self) -> bool {
matches!(DeviceType::from_i32(self.atype), DeviceType::Android | DeviceType::Ios)
}

pub fn is_registered(&self) -> bool {
self.push_uuid.is_some()
}
}

use crate::db::DbConn;
Expand Down Expand Up @@ -210,6 +218,7 @@ impl Device {
.from_db()
}}
}

pub async fn find_push_devices_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
devices::table
Expand Down

0 comments on commit 6f28af7

Please sign in to comment.