fxa_client/
device.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
5//! # Device Management
6//!
7//! Applications that connect to a user's account may register additional information
8//! about themselves via a "device record", which allows them to:
9//!
10//!    - customize how they appear in the user's account management page
11//!    - receive push notifications about events that happen on the account
12//!    - participate in the FxA "device commands" ecosystem
13//!
14//! For more details on FxA device registration and management, consult the
15//! [Firefox Accounts Device Registration docs](
16//! https://github.com/mozilla/fxa/blob/main/packages/fxa-auth-server/docs/device_registration.md).
17
18use error_support::handle_error;
19use serde::{Deserialize, Serialize};
20use sync15::DeviceType;
21
22use crate::{ApiResult, DevicePushSubscription, Error, FirefoxAccount};
23
24impl FirefoxAccount {
25    /// Create a new device record for this application.
26    ///
27    /// **💾 This method alters the persisted account state.**
28    ///
29    /// This method register a device record for the application, providing basic metadata for
30    /// the device along with a list of supported [Device Capabilities](DeviceCapability) for
31    /// participating in the "device commands" ecosystem.
32    ///
33    /// Applications should call this method soon after a successful sign-in, to ensure
34    /// they they appear correctly in the user's account-management pages and when discovered
35    /// by other devices connected to the account.
36    ///
37    /// # Arguments
38    ///
39    ///    - `name` - human-readable display name to use for this application
40    ///    - `device_type` - the [type](DeviceType) of device the application is installed on
41    ///    - `supported_capabilities` - the set of [capabilities](DeviceCapability) to register
42    ///       for this device in the "device commands" ecosystem.
43    ///
44    /// # Notes
45    ///
46    ///    - Device registration is only available to applications that have been
47    ///      granted the `https://identity.mozilla.com/apps/oldsync` scope.
48    #[handle_error(Error)]
49    pub fn initialize_device(
50        &self,
51        name: &str,
52        device_type: DeviceType,
53        supported_capabilities: Vec<DeviceCapability>,
54    ) -> ApiResult<LocalDevice> {
55        // UniFFI doesn't have good handling of lists of references, work around it.
56        let supported_capabilities: Vec<_> = supported_capabilities.into_iter().collect();
57        self.internal
58            .lock()
59            .initialize_device(name, device_type, &supported_capabilities)
60    }
61
62    /// Get the device id registered for this application.
63    ///
64    /// # Notes
65    ///
66    ///    - If the application has not registered a device record, this method will
67    ///      throw an [`Other`](FxaError::Other) error.
68    ///        - (Yeah...sorry. This should be changed to do something better.)
69    ///    - Device metadata is only visible to applications that have been
70    ///      granted the `https://identity.mozilla.com/apps/oldsync` scope.
71    #[handle_error(Error)]
72    pub fn get_current_device_id(&self) -> ApiResult<String> {
73        self.internal.lock().get_current_device_id()
74    }
75
76    /// Get the list of devices registered on the user's account.
77    ///
78    /// **💾 This method alters the persisted account state.**
79    ///
80    /// This method returns a list of [`Device`] structs representing all the devices
81    /// currently attached to the user's account (including the current device).
82    /// The application might use this information to e.g. display a list of appropriate
83    /// send-tab targets.
84    ///
85    /// # Arguments
86    ///
87    ///    - `ignore_cache` - if true, always hit the server for fresh profile information.
88    ///
89    /// # Notes
90    ///
91    ///    - Device metadata is only visible to applications that have been
92    ///      granted the `https://identity.mozilla.com/apps/oldsync` scope.
93    #[handle_error(Error)]
94    pub fn get_devices(&self, ignore_cache: bool) -> ApiResult<Vec<Device>> {
95        self.internal
96            .lock()
97            .get_devices(ignore_cache)?
98            .into_iter()
99            .map(TryInto::try_into)
100            .collect::<Result<_, _>>()
101    }
102
103    /// Get the list of all client applications attached to the user's account.
104    ///
105    /// This method returns a list of [`AttachedClient`] structs representing all the applications
106    /// connected to the user's account. This includes applications that are registered as a device
107    /// as well as server-side services that the user has connected.
108    ///
109    /// This information is really only useful for targeted messaging or marketing purposes,
110    /// e.g. if the application wants to advertise a related product, but first wants to check
111    /// whether the user is already using that product.
112    ///
113    /// # Notes
114    ///
115    ///    - Attached client metadata is only visible to applications that have been
116    ///      granted the `https://identity.mozilla.com/apps/oldsync` scope.
117    #[handle_error(Error)]
118    pub fn get_attached_clients(&self) -> ApiResult<Vec<AttachedClient>> {
119        self.internal
120            .lock()
121            .get_attached_clients()?
122            .into_iter()
123            .map(TryInto::try_into)
124            .collect::<Result<_, _>>()
125    }
126
127    /// Update the display name used for this application instance.
128    ///
129    /// **💾 This method alters the persisted account state.**
130    ///
131    /// This method modifies the name of the current application's device record, as seen by
132    /// other applications and in the user's account management pages.
133    ///
134    /// # Arguments
135    ///
136    ///    - `display_name` - the new name for the current device.
137    ///
138    /// # Notes
139    ///
140    ///    - Device registration is only available to applications that have been
141    ///      granted the `https://identity.mozilla.com/apps/oldsync` scope.
142    #[handle_error(Error)]
143    pub fn set_device_name(&self, display_name: &str) -> ApiResult<LocalDevice> {
144        self.internal.lock().set_device_name(display_name)
145    }
146
147    /// Clear any custom display name used for this application instance.
148    ///
149    /// **💾 This method alters the persisted account state.**
150    ///
151    /// This method clears the name of the current application's device record, causing other
152    /// applications or the user's account management pages to have to fill in some sort of
153    /// default name when displaying this device.
154    ///
155    /// # Notes
156    ///
157    ///    - Device registration is only available to applications that have been
158    ///      granted the `https://identity.mozilla.com/apps/oldsync` scope.
159    #[handle_error(Error)]
160    pub fn clear_device_name(&self) -> ApiResult<()> {
161        self.internal.lock().clear_device_name()
162    }
163
164    /// Ensure that the device record has a specific set of capabilities.
165    ///
166    /// **💾 This method alters the persisted account state.**
167    ///
168    /// This method checks that the currently-registered device record is advertising the
169    /// given set of capabilities in the FxA "device commands" ecosystem. If not, then it
170    /// updates the device record to do so.
171    ///
172    /// Applications should call this method on each startup as a way to ensure that their
173    /// expected set of capabilities is being accurately reflected on the FxA server, and
174    /// to handle the rollout of new capabilities over time.
175    ///
176    /// # Arguments
177    ///
178    ///    - `supported_capabilities` - the set of [capabilities](DeviceCapability) to register
179    ///       for this device in the "device commands" ecosystem.
180    ///
181    /// # Notes
182    ///
183    ///    - Device registration is only available to applications that have been
184    ///      granted the `https://identity.mozilla.com/apps/oldsync` scope.
185    #[handle_error(Error)]
186    pub fn ensure_capabilities(
187        &self,
188        supported_capabilities: Vec<DeviceCapability>,
189    ) -> ApiResult<LocalDevice> {
190        let supported_capabilities: Vec<_> = supported_capabilities.into_iter().collect();
191        self.internal
192            .lock()
193            .ensure_capabilities(&supported_capabilities)
194    }
195}
196
197/// Device configuration
198#[derive(Clone, Debug, PartialEq, Eq)]
199pub struct DeviceConfig {
200    pub name: String,
201    pub device_type: sync15::DeviceType,
202    pub capabilities: Vec<DeviceCapability>,
203}
204
205/// Local device that's connecting to FxA
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct LocalDevice {
208    pub id: String,
209    pub display_name: String,
210    pub device_type: sync15::DeviceType,
211    pub capabilities: Vec<DeviceCapability>,
212    pub push_subscription: Option<DevicePushSubscription>,
213    pub push_endpoint_expired: bool,
214}
215
216/// A device connected to the user's account.
217///
218/// This struct provides metadata about a device connected to the user's account.
219/// This data would typically be used to display e.g. the list of candidate devices
220/// in a "send tab" menu.
221#[derive(Debug)]
222pub struct Device {
223    pub id: String,
224    pub display_name: String,
225    pub device_type: sync15::DeviceType,
226    pub capabilities: Vec<DeviceCapability>,
227    pub push_subscription: Option<DevicePushSubscription>,
228    pub push_endpoint_expired: bool,
229    pub is_current_device: bool,
230    pub last_access_time: Option<i64>,
231}
232
233/// A "capability" offered by a device.
234///
235/// In the FxA ecosystem, connected devices may advertise their ability to respond
236/// to various "commands" that can be invoked by other devices. The details of
237/// executing these commands are encapsulated as part of the FxA Client component,
238/// so consumers simply need to select which ones they want to support, and can
239/// use the variants of this enum to do so.
240#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
241pub enum DeviceCapability {
242    SendTab,
243    CloseTabs,
244}
245
246/// A client connected to the user's account.
247///
248/// This struct provides metadata about a client connected to the user's account.
249/// Unlike the [`Device`] struct, "clients" encompasses both client-side and server-side
250/// applications - basically anything where the user is able to sign in with their
251/// Firefox Account.
252///
253///
254/// This data would typically be used for targeted messaging purposes, catering the
255/// contents of the message to what other applications the user has on their account.
256///
257pub struct AttachedClient {
258    pub client_id: Option<String>,
259    pub device_id: Option<String>,
260    pub device_type: DeviceType,
261    pub is_current_session: bool,
262    pub name: Option<String>,
263    pub created_time: Option<i64>,
264    pub last_access_time: Option<i64>,
265    pub scope: Option<Vec<String>>,
266}
267
268#[derive(Clone, Debug, PartialEq, Eq)]
269pub enum CloseTabsResult {
270    Ok,
271    TabsNotClosed { urls: Vec<String> },
272}