Skip to main content

libsignal_service/push_service/
linking.rs

1use libsignal_core::DeviceId;
2use reqwest::Method;
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6use crate::{
7    configuration::Endpoint, utils::serde_device_id,
8    websocket::registration::DeviceActivationRequest,
9};
10
11use super::{
12    response::ReqwestExt, HttpAuth, HttpAuthOverride, PushService, ServiceError,
13};
14
15#[derive(Debug, Serialize)]
16#[serde(rename_all = "camelCase")]
17pub struct LinkAccountAttributes {
18    pub fetches_messages: bool,
19    pub name: String,
20    pub registration_id: u32,
21    pub pni_registration_id: u32,
22    pub capabilities: LinkCapabilities,
23}
24
25#[derive(Debug, Serialize)]
26#[serde(rename_all = "camelCase")]
27// Keep in sync with https://github.com/signalapp/Signal-Desktop/blob/main/ts/types/Capabilities.d.ts.
28pub struct LinkCapabilities {
29    pub attachment_backfill: bool,
30    /// Sparse Post-Quantum Ratchet (`SPARSE_POST_QUANTUM_RATCHET` on Signal Server).
31    ///
32    /// Required for all devices; the server returns 409 if a linking device omits this capability
33    /// while the account already has it on any existing device.
34    pub spqr: bool,
35    pub username_change_sync_message: bool,
36}
37
38// https://github.com/signalapp/Signal-Desktop/blob/1e57db6aa4786dcddc944349e4894333ac2ffc9e/ts/textsecure/WebAPI.ts#L1287
39impl Default for LinkCapabilities {
40    fn default() -> Self {
41        Self {
42            attachment_backfill: false,
43            spqr: true,
44            username_change_sync_message: true,
45        }
46    }
47}
48
49#[derive(Debug, Deserialize)]
50#[serde(rename_all = "camelCase")]
51pub struct LinkResponse {
52    #[serde(rename = "uuid")]
53    pub aci: Uuid,
54    pub pni: Uuid,
55    #[serde(with = "serde_device_id")]
56    pub device_id: DeviceId,
57}
58
59#[derive(Debug, Serialize)]
60#[serde(rename_all = "camelCase")]
61pub struct LinkRequest {
62    pub verification_code: String,
63    pub account_attributes: LinkAccountAttributes,
64    #[serde(flatten)]
65    pub device_activation_request: DeviceActivationRequest,
66}
67
68impl PushService {
69    pub async fn link_device(
70        &mut self,
71        link_request: &LinkRequest,
72        http_auth: HttpAuth,
73    ) -> Result<LinkResponse, ServiceError> {
74        self.request(
75            Method::PUT,
76            Endpoint::service("/v1/devices/link"),
77            HttpAuthOverride::Identified(http_auth),
78        )?
79        .json(&link_request)
80        .send()
81        .await?
82        .service_error_for_status()
83        .await?
84        .json()
85        .await
86        .map_err(Into::into)
87    }
88}