fxa_client/
push.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use error_support::handle_error;
6use serde::{Deserialize, Serialize};
7
8use crate::{internal, ApiResult, CloseTabsResult, Device, Error, FirefoxAccount, LocalDevice};
9
10impl FirefoxAccount {
11    /// Set or update a push subscription endpoint for this device.
12    ///
13    /// **💾 This method alters the persisted account state.**
14    ///
15    /// This method registers the given webpush subscription with the FxA server, requesting
16    /// that is send notifications in the event of any significant changes to the user's
17    /// account. When the application receives a push message at the registered subscription
18    /// endpoint, it should decrypt the payload and pass it to the [`handle_push_message`](
19    /// FirefoxAccount::handle_push_message) method for processing.
20    ///
21    /// # Arguments
22    ///
23    ///    - `subscription` - the [`DevicePushSubscription`] details to register with the server.
24    ///
25    /// # Notes
26    ///
27    ///    - Device registration is only available to applications that have been
28    ///      granted the `https://identity.mozilla.com/apps/oldsync` scope.
29    #[handle_error(Error)]
30    pub fn set_push_subscription(
31        &self,
32        subscription: DevicePushSubscription,
33    ) -> ApiResult<LocalDevice> {
34        self.internal
35            .lock()
36            .set_push_subscription(subscription.into())
37    }
38
39    /// Process and respond to a server-delivered account update message
40    ///
41    /// **💾 This method alters the persisted account state.**
42    ///
43    /// Applications should call this method whenever they receive a push notification from the Firefox Accounts server.
44    /// Such messages typically indicate a noteworthy change of state on the user's account, such as an update to their profile information
45    /// or the disconnection of a client. The [`FirefoxAccount`] struct will update its internal state
46    /// accordingly and return an individual [`AccountEvent`] struct describing the event, which the application
47    /// may use for further processing.
48    ///
49    /// It's important to note if the event is [`AccountEvent::CommandReceived`], the caller should call
50    /// [`FirefoxAccount::poll_device_commands`]
51    #[handle_error(Error)]
52    pub fn handle_push_message(&self, payload: &str) -> ApiResult<AccountEvent> {
53        self.internal.lock().handle_push_message(payload)
54    }
55
56    /// Poll the server for any pending device commands.
57    ///
58    /// **💾 This method alters the persisted account state.**
59    ///
60    /// Applications that have registered one or more [`DeviceCapability`]s with the server can use
61    /// this method to check whether other devices on the account have sent them any commands.
62    /// It will return a list of [`IncomingDeviceCommand`] structs for the application to process.
63    ///
64    /// # Notes
65    ///
66    ///    - Device commands are typically delivered via push message and the [`CommandReceived`](
67    ///      AccountEvent::CommandReceived) event. Polling should only be used as a backup delivery
68    ///      mechanism, f the application has reason to believe that push messages may have been missed.
69    ///    - Device commands functionality is only available to applications that have been
70    ///      granted the `https://identity.mozilla.com/apps/oldsync` scope.
71    #[handle_error(Error)]
72    pub fn poll_device_commands(&self) -> ApiResult<Vec<IncomingDeviceCommand>> {
73        self.internal
74            .lock()
75            .poll_device_commands(internal::device::CommandFetchReason::Poll)?
76            .into_iter()
77            .map(TryFrom::try_from)
78            .collect::<Result<_, _>>()
79    }
80
81    /// Use device commands to send a single tab to another device.
82    ///
83    /// **💾 This method alters the persisted account state.**
84    ///
85    /// If a device on the account has registered the [`SendTab`](DeviceCapability::SendTab)
86    /// capability, this method can be used to send it a tab.
87    ///
88    /// # Notes
89    ///
90    ///    - If the given device id does not existing or is not capable of receiving tabs,
91    ///      this method will throw an [`Other`](FxaError::Other) error.
92    ///        - (Yeah...sorry. This should be changed to do something better.)
93    ///    - It is not currently possible to send a full [`SendTabPayload`] to another device,
94    ///      but that's purely an API limitation that should go away in future.
95    ///    - Device commands functionality is only available to applications that have been
96    ///      granted the `https://identity.mozilla.com/apps/oldsync` scope.
97    #[handle_error(Error)]
98    pub fn send_single_tab(
99        &self,
100        target_device_id: &str,
101        title: &str,
102        url: &str,
103        private: bool,
104    ) -> ApiResult<()> {
105        self.internal
106            .lock()
107            .send_single_tab(target_device_id, title, url, private)
108    }
109
110    /// Use device commands to close one or more tabs on another device.
111    ///
112    /// **💾 This method alters the persisted account state.**
113    ///
114    /// If a device on the account has registered the [`CloseTabs`](DeviceCapability::CloseTabs)
115    /// capability, this method can be used to close its tabs.
116    #[handle_error(Error)]
117    pub fn close_tabs(
118        &self,
119        target_device_id: &str,
120        urls: Vec<String>,
121    ) -> ApiResult<CloseTabsResult> {
122        self.internal.lock().close_tabs(target_device_id, urls)
123    }
124}
125
126/// Details of a web-push subscription endpoint.
127///
128/// This struct encapsulates the details of a web-push subscription endpoint,
129/// including all the information necessary to send a notification to its owner.
130/// Devices attached to the user's account may register one of these in order
131/// to receive timely updates about account-related events.
132///
133/// Managing a web-push subscription is outside of the scope of this component.
134///
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct DevicePushSubscription {
137    pub endpoint: String,
138    pub public_key: String,
139    pub auth_key: String,
140}
141
142/// An event that happened on the user's account.
143///
144/// If the application has registered a [`DevicePushSubscription`] as part of its
145/// device record, then the Firefox Accounts server can send push notifications
146/// about important events that happen on the user's account. This enum represents
147/// the different kinds of event that can occur.
148///
149// Clippy suggests we Box<> the CommandReceiver variant here,
150// but UniFFI isn't able to look through boxes yet, so we
151// disable the warning.
152#[allow(clippy::large_enum_variant)]
153#[derive(Debug)]
154pub enum AccountEvent {
155    /// Sent when another device has invoked a command for this device to execute.
156    ///
157    /// When receiving this event, the application should inspect the contained
158    /// command and react appropriately.
159    CommandReceived { command: IncomingDeviceCommand },
160    /// Sent when the user has modified their account profile information.
161    ///
162    /// When receiving this event, the application should request fresh profile
163    /// information by calling [`get_profile`](FirefoxAccount::get_profile) with
164    /// `ignore_cache` set to true, and update any profile information displayed
165    /// in its UI.
166    ///
167    ProfileUpdated,
168    /// Sent when when there has been a change in authorization status.
169    ///
170    /// When receiving this event, the application should check whether it is
171    /// still connected to the user's account by calling [`check_authorization_status`](
172    /// FirefoxAccount::check_authorization_status), and updating its UI as appropriate.
173    ///
174    AccountAuthStateChanged,
175    /// Sent when the user deletes their Firefox Account.
176    ///
177    /// When receiving this event, the application should act as though the user had
178    /// signed out, discarding any persisted account state.
179    AccountDestroyed,
180    /// Sent when a new device connects to the user's account.
181    ///
182    /// When receiving this event, the application may use it to trigger an update
183    /// of any UI that shows the list of connected devices. It may also show the
184    /// user an informational notice about the new device, as a security measure.
185    DeviceConnected { device_name: String },
186    /// Sent when a device disconnects from the user's account.
187    ///
188    /// When receiving this event, the application may use it to trigger an update
189    /// of any UI that shows the list of connected devices.
190    DeviceDisconnected {
191        device_id: String,
192        is_local_device: bool,
193    },
194
195    /// An unknown event, most likely an event the client doesn't support yet.
196    ///
197    /// When receiving this event, the application should gracefully ignore it.
198    Unknown,
199}
200
201/// A command invoked by another device.
202///
203/// This enum represents all possible commands that can be invoked on
204/// the device. It is the responsibility of the application to interpret
205/// each command.
206#[derive(Debug)]
207pub enum IncomingDeviceCommand {
208    /// Indicates that a tab has been sent to this device.
209    TabReceived {
210        sender: Option<Device>,
211        payload: SendTabPayload,
212    },
213    TabsClosed {
214        sender: Option<Device>,
215        payload: CloseTabsPayload,
216    },
217}
218
219/// The payload sent when invoking a "send tab" command.
220#[derive(Debug)]
221pub struct SendTabPayload {
222    /// The navigation history of the sent tab.
223    ///
224    /// The last item in this list represents the page to be displayed,
225    /// while earlier items may be included in the navigation history
226    /// as a convenience to the user.
227    pub entries: Vec<TabHistoryEntry>,
228    /// A unique identifier to be included in send-tab metrics.
229    ///
230    /// The application should treat this as opaque.
231    pub flow_id: String,
232    /// A unique identifier to be included in send-tab metrics.
233    ///
234    /// The application should treat this as opaque.
235    pub stream_id: String,
236}
237
238/// The payload sent when invoking a "close tabs" command.
239#[derive(Debug)]
240pub struct CloseTabsPayload {
241    pub urls: Vec<String>,
242}
243
244/// An individual entry in the navigation history of a sent tab.
245#[derive(Debug)]
246pub struct TabHistoryEntry {
247    pub title: String,
248    pub url: String,
249    pub is_private: bool,
250}