libsignal_service/websocket/
account.rs

1use chrono::{DateTime, Utc};
2use reqwest::Method;
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6use crate::{
7    content::ServiceError,
8    proto::DeviceName,
9    utils::{
10        serde_device_id, serde_e164, serde_optional_base64,
11        serde_optional_prost_base64,
12    },
13    websocket,
14};
15
16use super::SignalWebSocket;
17
18#[derive(Debug, Serialize, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub struct DeviceId {
21    #[serde(with = "serde_device_id")]
22    pub device_id: libsignal_core::DeviceId,
23}
24
25#[derive(Debug, Serialize, Deserialize)]
26#[serde(rename_all = "camelCase")]
27pub struct DeviceInfo {
28    #[serde(with = "serde_device_id")]
29    pub id: libsignal_core::DeviceId,
30    pub registration_id: i32,
31    pub name: Option<String>,
32    #[serde(with = "chrono::serde::ts_milliseconds")]
33    pub created_at: DateTime<Utc>,
34    #[serde(with = "chrono::serde::ts_milliseconds")]
35    pub last_seen: DateTime<Utc>,
36}
37
38#[derive(Debug, Serialize, Deserialize)]
39#[serde(rename_all = "camelCase")]
40pub(crate) struct DeviceInfoEncrypted {
41    #[serde(with = "serde_device_id")]
42    pub id: libsignal_core::DeviceId,
43    pub name: Option<String>,
44    pub registration_id: i32,
45    pub created_at_ciphertext: String,
46    #[serde(with = "chrono::serde::ts_milliseconds")]
47    pub last_seen: DateTime<Utc>,
48}
49
50#[derive(Debug, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52/// kept in sync with https://github.com/signalapp/Signal-Server/blob/main/service/src/main/java/org/whispersystems/textsecuregcm/entities/AccountAttributes.java#L25
53pub struct AccountAttributes {
54    pub fetches_messages: bool,
55    pub registration_id: u32,
56    pub pni_registration_id: u32,
57    #[serde(default, with = "serde_optional_prost_base64")]
58    pub name: Option<DeviceName>,
59    #[serde(default, skip_serializing_if = "Option::is_none")]
60    pub registration_lock: Option<String>,
61    #[serde(default, with = "serde_optional_base64")]
62    pub unidentified_access_key: Option<Vec<u8>>,
63    pub unrestricted_unidentified_access: bool,
64    pub capabilities: DeviceCapabilities,
65    pub discoverable_by_phone_number: bool,
66    pub pin: Option<String>,
67    #[serde(
68        default,
69        with = "serde_optional_base64",
70        skip_serializing_if = "Option::is_none"
71    )]
72    pub recovery_password: Option<Vec<u8>>,
73}
74
75#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
76#[serde(rename_all = "camelCase")]
77pub struct DeviceCapabilities {
78    #[serde(default)]
79    pub storage: bool,
80    #[serde(default)]
81    pub transfer: bool,
82    #[serde(default)]
83    pub attachment_backfill: bool,
84    #[serde(default)]
85    pub spqr: bool,
86}
87
88impl Default for DeviceCapabilities {
89    fn default() -> Self {
90        DeviceCapabilities {
91            storage: false,
92            transfer: false,
93            attachment_backfill: false,
94            spqr: true,
95        }
96    }
97}
98
99#[derive(Debug, Deserialize)]
100#[serde(rename_all = "camelCase")]
101pub struct WhoAmIResponse {
102    #[serde(rename = "uuid")]
103    pub aci: Uuid,
104    #[serde(default)] // nil when not present (yet)
105    pub pni: Uuid,
106    #[serde(with = "serde_e164")]
107    pub number: libsignal_core::E164,
108}
109
110impl SignalWebSocket<websocket::Identified> {
111    /// Method used to check our own UUID
112    pub async fn whoami(&mut self) -> Result<WhoAmIResponse, ServiceError> {
113        self.http_request(Method::GET, "/v1/accounts/whoami")?
114            .send()
115            .await?
116            .service_error_for_status()
117            .await?
118            .json()
119            .await
120    }
121
122    /// Fetches a list of all devices tied to the authenticated account.
123    ///
124    /// This list include the device that sends the request.
125    pub(crate) async fn devices(
126        &mut self,
127    ) -> Result<Vec<DeviceInfoEncrypted>, ServiceError> {
128        #[derive(serde::Deserialize)]
129        struct DeviceInfoList {
130            devices: Vec<DeviceInfoEncrypted>,
131        }
132
133        let devices: DeviceInfoList = self
134            .http_request(Method::GET, "/v1/devices")?
135            .send()
136            .await?
137            .service_error_for_status()
138            .await?
139            .json()
140            .await?;
141
142        Ok(devices.devices)
143    }
144
145    pub async fn set_account_attributes(
146        &mut self,
147        attributes: AccountAttributes,
148    ) -> Result<(), ServiceError> {
149        assert!(
150            attributes.pin.is_none() || attributes.registration_lock.is_none(),
151            "only one of PIN and registration lock can be set."
152        );
153
154        self.http_request(Method::PUT, "/v1/accounts/attributes")?
155            .send_json(&attributes)
156            .await?
157            .service_error_for_status()
158            .await?;
159
160        Ok(())
161    }
162
163    /// Unregister and delete the account from Signal servers.
164    ///
165    /// This permanently deletes the account and all associated data (groups, contacts, messages).
166    /// After calling this, the phone number can be re-registered with a fresh account.
167    ///
168    /// CAUTION: This is irreversible. All account data will be lost.
169    pub async fn unregister_account(&mut self) -> Result<(), ServiceError> {
170        self.http_request(Method::DELETE, "/v1/accounts/me")?
171            .send()
172            .await?
173            .service_error_for_status()
174            .await?;
175
176        Ok(())
177    }
178}