fxa_client/
token.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//! # Token Management
6//!
7//! A signed-in application will typically hold a number of different *tokens* associated with the
8//! user's account, including:
9//!
10//!    - An OAuth `refresh_token`, representing their ongoing connection to the account
11//!      and the scopes that have been granted.
12//!    - Short-lived OAuth `access_token`s that can be used to access resources on behalf
13//!      of the user.
14//!    - Optionally, a `session_token` that gives full control over the user's account,
15//!      typically managed on behalf of web content that runs within the context
16//!      of the application.
17
18use crate::{ApiResult, Error, FirefoxAccount};
19use error_support::handle_error;
20use serde_derive::*;
21use std::convert::TryInto;
22
23#[uniffi::export]
24impl FirefoxAccount {
25    /// Get a short-lived OAuth access token for the user's account.
26    ///
27    /// **💾 This method alters the persisted account state.**
28    ///
29    /// Applications that need to access resources on behalf of the user must obtain an
30    /// `access_token` in order to do so. For example, an access token is required when
31    /// fetching the user's profile data, or when accessing their data stored in Firefox Sync.
32    ///
33    /// This method will obtain and return an access token bearing the requested scopes, either
34    /// from a local cache of previously-issued tokens, or by creating a new one from the server.
35    ///
36    /// # Arguments
37    ///
38    ///    - `scope` - space-separated list of OAuth scopes to be granted by the token.
39    ///        - Each scope must have been requested during the signin flow, or be a scope
40    ///          which the server might offer automatically in some account-specific cases.
41    ///        - Scope order is not significant; `"a b"` and `"b a"` are equivalent.
42    ///        - When a single scope is requested and it has an associated scoped key
43    ///          (e.g. `https://identity.mozilla.com/apps/oldsync`), the returned
44    ///          `AccessTokenInfo::key` will be populated; for multi-scope requests it is `None`.
45    ///    - `use_cache` - optionally set to false to force a new token request.  The fetched
46    ///       token will still be cached for later `get_access_token` calls.
47    ///
48    /// # Notes
49    ///
50    ///    - If the application receives an authorization error when trying to use the resulting
51    ///      token, it should call [`clear_access_token_cache`](FirefoxAccount::clear_access_token_cache)
52    ///      before requesting a fresh token.
53    #[handle_error(Error)]
54    #[uniffi::method(default(use_cache = true))]
55    pub fn get_access_token(&self, scope: &str, use_cache: bool) -> ApiResult<AccessTokenInfo> {
56        self.internal
57            .lock()
58            .get_access_token(scope, use_cache)?
59            .try_into()
60    }
61
62    /// Check whether the account has already been granted the given OAuth scope(s).
63    ///
64    /// This checks whether the refresh token has *every* specified scope.
65    ///
66    /// # Arguments
67    ///    - `scope` - space-separated list of OAuth scopes. Order is not significant.
68    pub fn has_scope(&self, scope: &str) -> bool {
69        self.internal.lock().has_scope(scope)
70    }
71
72    /// Builds a complete `signedInUser` JSON object for a WebChannel `fxaccounts:fxa_status`
73    /// response, embedding the session token without exposing it to the browser layer. Email and
74    /// uid are read from the cached profile in internal state. Returns `None` if no session token
75    /// is available.
76    pub fn get_signed_in_user_for_web_channel(&self) -> Option<String> {
77        self.internal.lock().get_signed_in_user_for_web_channel()
78    }
79
80    /// Handle a WebChannel password-change notification by exchanging the new session token
81    /// for a new refresh token.
82    ///
83    /// **💾 This method alters the persisted account state.**
84    #[handle_error(Error)]
85    pub fn handle_web_channel_password_change(&self, json_payload: String) -> ApiResult<()> {
86        self.internal
87            .lock()
88            .handle_web_channel_password_change(&json_payload)
89    }
90
91    /// Get the session token for the user's account, if one is available.
92    ///
93    /// **💾 This method alters the persisted account state.**
94    ///
95    /// Applications that function as a web browser may need to hold on to a session token
96    /// on behalf of Firefox Accounts web content. This method exists so that they can retrieve
97    /// it an pass it back to said web content when required.
98    ///
99    /// # Notes
100    ///
101    ///    - Please do not attempt to use the resulting token to directly make calls to the
102    ///      Firefox Accounts servers! All account management functionality should be performed
103    ///      in web content.
104    ///    - A session token is only available to applications that have requested the
105    ///      `https://identity.mozilla.com/tokens/session` scope.
106    #[handle_error(Error)]
107    pub fn get_session_token(&self) -> ApiResult<String> {
108        self.internal.lock().get_session_token()
109    }
110
111    /// Update the stored session token for the user's account.
112    ///
113    /// **💾 This method alters the persisted account state.**
114    ///
115    /// Applications that function as a web browser may need to hold on to a session token
116    /// on behalf of Firefox Accounts web content. This method exists so that said web content
117    /// signals that it has generated a new session token, the stored value can be updated
118    /// to match.
119    ///
120    /// # Arguments
121    ///
122    ///    - `session_token` - the new session token value provided from web content.
123    #[handle_error(Error)]
124    pub fn handle_session_token_change(&self, session_token: &str) -> ApiResult<()> {
125        self.internal
126            .lock()
127            .handle_session_token_change(session_token)
128    }
129
130    /// Create a new OAuth authorization code using the stored session token.
131    ///
132    /// When a signed-in application receives an incoming device pairing request, it can
133    /// use this method to grant the request and generate a corresponding OAuth authorization
134    /// code. This code would then be passed back to the connecting device over the
135    /// pairing channel (a process which is not currently supported by any code in this
136    /// component).
137    ///
138    /// # Arguments
139    ///
140    ///    - `params` - the OAuth parameters from the incoming authorization request
141    #[handle_error(Error)]
142    pub fn authorize_code_using_session_token(
143        &self,
144        params: AuthorizationParameters,
145    ) -> ApiResult<String> {
146        self.internal
147            .lock()
148            .authorize_code_using_session_token(params)
149    }
150
151    /// Clear the access token cache in response to an auth failure.
152    ///
153    /// **💾 This method alters the persisted account state.**
154    ///
155    /// Applications that receive an authentication error when trying to use an access token,
156    /// should call this method before creating a new token and retrying the failed operation.
157    /// It ensures that the expired token is removed and a fresh one generated.
158    pub fn clear_access_token_cache(&self) {
159        self.internal.lock().clear_access_token_cache()
160    }
161}
162
163#[derive(uniffi::Record, Debug)]
164/// An OAuth access token, with its associated keys and metadata.
165///
166/// This struct represents an FxA OAuth access token, which can be used to access a resource
167/// or service on behalf of the user. For example, accessing the user's data in Firefox Sync
168/// an access token for the scope `https://identity.mozilla.com/apps/sync` along with the
169/// associated encryption key.
170pub struct AccessTokenInfo {
171    /// The scope of access granted by token.
172    pub scope: String,
173    /// The access token itself.
174    ///
175    /// This is the value that should be included in the `Authorization` header when
176    /// accessing an OAuth protected resource on behalf of the user.
177    pub token: String,
178    /// The client-side encryption key associated with this scope.
179    ///
180    /// **⚠️ Warning:** the value of this field should never be revealed outside of the
181    /// application. For example, it should never to sent to a server or logged in a log file.
182    pub key: Option<ScopedKey>,
183    /// The expiry time of the token, in seconds.
184    ///
185    /// This is the timestamp at which the token is set to expire, in seconds since
186    /// unix epoch. Note that it is a signed integer, for compatibility with languages
187    /// that do not have an unsigned integer type.
188    ///
189    /// This timestamp is for guidance only. Access tokens are not guaranteed to remain
190    /// value for any particular lengthof time, and consumers should be prepared to handle
191    /// auth failures even if the token has not yet expired.
192    pub expires_at: i64,
193}
194
195#[derive(uniffi::Record, Clone, Serialize, Deserialize)]
196/// A cryptographic key associated with an OAuth scope.
197///
198/// Some OAuth scopes have a corresponding client-side encryption key that is required
199/// in order to access protected data. This struct represents such key material in a
200/// format compatible with the common "JWK" standard.
201pub struct ScopedKey {
202    /// The type of key.
203    ///
204    /// In practice for FxA, this will always be string string "oct" (short for "octal")
205    /// to represent a raw symmetric key.
206    pub kty: String,
207    /// The OAuth scope with which this key is associated.
208    pub scope: String,
209    /// The key material, as base64-url-encoded bytes.
210    ///
211    /// **⚠️ Warning:** the value of this field should never be revealed outside of the
212    /// application. For example, it should never to sent to a server or logged in a log file.
213    pub k: String,
214    /// An opaque unique identifier for this key.
215    ///
216    /// Unlike the `k` field, this value is not secret and may be revealed to the server.
217    pub kid: String,
218}
219
220#[derive(uniffi::Record)]
221/// Parameters provided in an incoming OAuth request.
222///
223/// This struct represents parameters obtained from an incoming OAuth request - that is,
224/// the values that an OAuth client would append to the authorization URL when initiating
225/// an OAuth sign-in flow.
226pub struct AuthorizationParameters {
227    pub client_id: String,
228    pub scope: Vec<String>,
229    pub state: String,
230    pub access_type: String,
231    pub code_challenge: Option<String>,
232    pub code_challenge_method: Option<String>,
233    pub keys_jwk: Option<String>,
234}