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 /// It will only return active sessions.
110 /// For example, if a user has disconnected the service from their account,
111 /// it wouldn't appear in this list.
112 #[handle_error(Error)]
113 pub fn get_attached_clients(&self) -> ApiResult<Vec<AttachedClient>> {
114 self.internal
115 .lock()
116 .get_attached_clients()?
117 .into_iter()
118 .map(TryInto::try_into)
119 .collect::<Result<_, _>>()
120 }
121
122 /// Update the display name used for this application instance.
123 ///
124 /// **💾 This method alters the persisted account state.**
125 ///
126 /// This method modifies the name of the current application's device record, as seen by
127 /// other applications and in the user's account management pages.
128 ///
129 /// # Arguments
130 ///
131 /// - `display_name` - the new name for the current device.
132 ///
133 /// # Notes
134 ///
135 /// - Device registration is only available to applications that have been
136 /// granted the `https://identity.mozilla.com/apps/oldsync` scope.
137 #[handle_error(Error)]
138 pub fn set_device_name(&self, display_name: &str) -> ApiResult<LocalDevice> {
139 self.internal.lock().set_device_name(display_name)
140 }
141
142 /// Clear any custom display name used for this application instance.
143 ///
144 /// **💾 This method alters the persisted account state.**
145 ///
146 /// This method clears the name of the current application's device record, causing other
147 /// applications or the user's account management pages to have to fill in some sort of
148 /// default name when displaying this device.
149 ///
150 /// # Notes
151 ///
152 /// - Device registration is only available to applications that have been
153 /// granted the `https://identity.mozilla.com/apps/oldsync` scope.
154 #[handle_error(Error)]
155 pub fn clear_device_name(&self) -> ApiResult<()> {
156 self.internal.lock().clear_device_name()
157 }
158
159 /// Ensure that the device record has a specific set of capabilities.
160 ///
161 /// **💾 This method alters the persisted account state.**
162 ///
163 /// This method checks that the currently-registered device record is advertising the
164 /// given set of capabilities in the FxA "device commands" ecosystem. If not, then it
165 /// updates the device record to do so.
166 ///
167 /// Applications should call this method on each startup as a way to ensure that their
168 /// expected set of capabilities is being accurately reflected on the FxA server, and
169 /// to handle the rollout of new capabilities over time.
170 ///
171 /// # Arguments
172 ///
173 /// - `supported_capabilities` - the set of [capabilities](DeviceCapability) to register
174 /// for this device in the "device commands" ecosystem.
175 ///
176 /// # Notes
177 ///
178 /// - Device registration is only available to applications that have been
179 /// granted the `https://identity.mozilla.com/apps/oldsync` scope.
180 #[handle_error(Error)]
181 pub fn ensure_capabilities(
182 &self,
183 supported_capabilities: Vec<DeviceCapability>,
184 ) -> ApiResult<LocalDevice> {
185 let supported_capabilities: Vec<_> = supported_capabilities.into_iter().collect();
186 self.internal
187 .lock()
188 .ensure_capabilities(&supported_capabilities)
189 }
190}
191
192/// Device configuration
193#[derive(Clone, Debug, PartialEq, Eq)]
194pub struct DeviceConfig {
195 pub name: String,
196 pub device_type: sync15::DeviceType,
197 pub capabilities: Vec<DeviceCapability>,
198}
199
200/// Local device that's connecting to FxA
201#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct LocalDevice {
203 pub id: String,
204 pub display_name: String,
205 pub device_type: sync15::DeviceType,
206 pub capabilities: Vec<DeviceCapability>,
207 pub push_subscription: Option<DevicePushSubscription>,
208 pub push_endpoint_expired: bool,
209}
210
211/// A device connected to the user's account.
212///
213/// This struct provides metadata about a device connected to the user's account.
214/// This data would typically be used to display e.g. the list of candidate devices
215/// in a "send tab" menu.
216#[derive(Debug)]
217pub struct Device {
218 pub id: String,
219 pub display_name: String,
220 pub device_type: sync15::DeviceType,
221 pub capabilities: Vec<DeviceCapability>,
222 pub push_subscription: Option<DevicePushSubscription>,
223 pub push_endpoint_expired: bool,
224 pub is_current_device: bool,
225 pub last_access_time: Option<i64>,
226}
227
228/// A "capability" offered by a device.
229///
230/// In the FxA ecosystem, connected devices may advertise their ability to respond
231/// to various "commands" that can be invoked by other devices. The details of
232/// executing these commands are encapsulated as part of the FxA Client component,
233/// so consumers simply need to select which ones they want to support, and can
234/// use the variants of this enum to do so.
235#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
236pub enum DeviceCapability {
237 SendTab,
238 CloseTabs,
239}
240
241/// A client connected to the user's account.
242///
243/// This struct provides metadata about a client connected to the user's account.
244/// Unlike the [`Device`] struct, "clients" encompasses both client-side and server-side
245/// applications - basically anything where the user is able to sign in with their
246/// Firefox Account.
247///
248///
249/// This data would typically be used for targeted messaging purposes, catering the
250/// contents of the message to what other applications the user has on their account.
251///
252pub struct AttachedClient {
253 pub client_id: Option<String>,
254 pub device_id: Option<String>,
255 pub device_type: DeviceType,
256 pub is_current_session: bool,
257 pub name: Option<String>,
258 pub created_time: Option<i64>,
259 pub last_access_time: Option<i64>,
260 pub scope: Option<Vec<String>>,
261}
262
263#[derive(Clone, Debug, PartialEq, Eq)]
264pub enum CloseTabsResult {
265 Ok,
266 TabsNotClosed { urls: Vec<String> },
267}