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::response::{
12    device_limit_reached, error_mapper, SignalServiceResponse,
13};
14use super::{HttpAuth, HttpAuthOverride, PushService, ServiceError};
15
16#[derive(Debug, Serialize)]
17#[serde(rename_all = "camelCase")]
18pub struct LinkAccountAttributes {
19    pub fetches_messages: bool,
20    pub name: String,
21    pub registration_id: u32,
22    pub pni_registration_id: u32,
23    pub capabilities: LinkCapabilities,
24}
25
26#[derive(Debug, Serialize)]
27#[serde(rename_all = "camelCase")]
28// Keep in sync with https://github.com/signalapp/Signal-Desktop/blob/main/ts/types/Capabilities.d.ts.
29pub struct LinkCapabilities {
30    pub attachment_backfill: bool,
31    /// Sparse Post-Quantum Ratchet (`SPARSE_POST_QUANTUM_RATCHET` on Signal Server).
32    ///
33    /// Required for all devices; the server returns 409 if a linking device omits this capability
34    /// while the account already has it on any existing device.
35    pub spqr: bool,
36    pub username_change_sync_message: bool,
37}
38
39// https://github.com/signalapp/Signal-Desktop/blob/1e57db6aa4786dcddc944349e4894333ac2ffc9e/ts/textsecure/WebAPI.ts#L1287
40impl Default for LinkCapabilities {
41    fn default() -> Self {
42        Self {
43            attachment_backfill: false,
44            spqr: true,
45            username_change_sync_message: true,
46        }
47    }
48}
49
50#[derive(Debug, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct LinkResponse {
53    #[serde(rename = "uuid")]
54    pub aci: Uuid,
55    pub pni: Uuid,
56    #[serde(with = "serde_device_id")]
57    pub device_id: DeviceId,
58}
59
60#[derive(Debug, Serialize)]
61#[serde(rename_all = "camelCase")]
62pub struct LinkRequest {
63    pub verification_code: String,
64    pub account_attributes: LinkAccountAttributes,
65    #[serde(flatten)]
66    pub device_activation_request: DeviceActivationRequest,
67}
68
69// Signal-Server: controllers/DeviceController.java:231
70// (PUT /v1/devices/link)
71error_mapper! {
72    link_device_errors:
73        // 403: invalid device verification code, DeviceController.java:238
74        FORBIDDEN => InvalidDeviceVerificationCode,
75        // 409: device capability downgrade, DeviceController.java:267
76        CONFLICT => DeviceCapabilityDowngrade,
77        // 411: device limit reached, DeviceController.java:260
78        LENGTH_REQUIRED => fn device_limit_reached,
79}
80
81impl PushService {
82    pub async fn link_device(
83        &mut self,
84        link_request: &LinkRequest,
85        http_auth: HttpAuth,
86    ) -> Result<LinkResponse, ServiceError> {
87        self.request(
88            Method::PUT,
89            Endpoint::service("/v1/devices/link"),
90            HttpAuthOverride::Identified(http_auth),
91        )?
92        .json(&link_request)
93        .send()
94        .await?
95        .service_error_for_status_with(link_device_errors)
96        .await?
97        .json()
98        .await
99        .map_err(Into::into)
100    }
101}