From 48b66d07bb8917d61f06c01294647b7fce163b5b Mon Sep 17 00:00:00 2001 From: Sammy Khamis Date: Wed, 3 Jun 2026 11:09:39 -1000 Subject: [PATCH] Remove public OAuth API, direct to go through state machine --- .../appservices/fxaclient/FxaClient.kt | 49 +------- components/fxa-client/src/auth.rs | 106 ++---------------- components/fxa-client/src/fxa_client.udl | 78 +------------ components/fxa-client/src/lib.rs | 8 +- 4 files changed, 16 insertions(+), 225 deletions(-) diff --git a/components/fxa-client/android/src/main/java/mozilla/appservices/fxaclient/FxaClient.kt b/components/fxa-client/android/src/main/java/mozilla/appservices/fxaclient/FxaClient.kt index 846a416c3c..31265a4b67 100644 --- a/components/fxa-client/android/src/main/java/mozilla/appservices/fxaclient/FxaClient.kt +++ b/components/fxa-client/android/src/main/java/mozilla/appservices/fxaclient/FxaClient.kt @@ -100,40 +100,6 @@ class FxaClient(inner: FirefoxAccount, persistCallback: PersistCallback?) : Auto */ fun getAuthState() = this.inner.getAuthState() - /** - * Constructs a URL used to begin the OAuth flow for the requested scopes and keys. - * - * This performs network requests, and should not be used on the main thread. - * - * @param scopes List of OAuth scopes for which the client wants access - * @param entrypoint to be used for metrics - * @return String that resolves to the flow URL when complete - */ - fun beginOAuthFlow( - scopes: Array, - entrypoint: String, - ): String { - return this.inner.beginOauthFlow(scopes.toList(), entrypoint) - } - - /** - * Begins the pairing flow. - * - * This performs network requests, and should not be used on the main thread. - * - * @param pairingUrl the url to initilaize the paring flow with - * @param scopes List of OAuth scopes for which the client wants access - * @param entrypoint to be used for metrics - * @return String that resoles to the flow URL when complete - */ - fun beginPairingFlow( - pairingUrl: String, - scopes: Array, - entrypoint: String, - ): String { - return this.inner.beginPairingFlow(pairingUrl, scopes.toList(), entrypoint) - } - /** * Stores anything necessary to login from a WebChannel login JSON payload. This includes the session * token, but that is abstracted because the consuming apps should not be aware of the @@ -167,19 +133,6 @@ class FxaClient(inner: FirefoxAccount, persistCallback: PersistCallback?) : Auto return this.inner.getSignedInUserForWebChannel() } - /** - * Authenticates the current account using the code and state parameters fetched from the - * redirect URL reached after completing the sign in flow triggered by [beginOAuthFlow]. - * - * Modifies the FirefoxAccount state. - * - * This performs network requests, and should not be used on the main thread. - */ - fun completeOAuthFlow(code: String, state: String) { - this.inner.completeOauthFlow(code, state) - this.tryPersistState() - } - /** * Fetches the profile object for the current client either from the existing cached account, * or from the server (requires the client to have access to the profile scope). @@ -408,7 +361,7 @@ class FxaClient(inner: FirefoxAccount, persistCallback: PersistCallback?) : Auto /** * Disconnect from the account and optionally destroy our device record. - * `beginOAuthFlow` will need to be called to reconnect. + * A `BeginOAuthFlow` state-machine event will need to be sent to reconnect. * * This performs network requests, and should not be used on the main thread. */ diff --git a/components/fxa-client/src/auth.rs b/components/fxa-client/src/auth.rs index 5d039344fe..136d38c3b1 100644 --- a/components/fxa-client/src/auth.rs +++ b/components/fxa-client/src/auth.rs @@ -4,21 +4,21 @@ //! # Signing in and out //! -//! These are methods for managing the signed-in state, such as authenticating via -//! an OAuth flow or disconnecting from the user's account. +//! Signing in and out is driven through the state machine: by sending the relevant +//! [`FxaEvent`] to [`FirefoxAccount::process_event`]. //! //! The Firefox Accounts system supports two methods for connecting an application //! to a user's account: //! //! - A traditional OAuth flow, where the user is directed to a webpage to enter //! their account credentials and then redirected back to the application. -//! This is exposed by the [`begin_oauth_flow`](FirefoxAccount::begin_oauth_flow) -//! method. +//! This is driven by the [`FxaEvent::BeginOAuthFlow`] and +//! [`FxaEvent::CompleteOAuthFlow`] events. //! //! - A device pairing flow, where the user scans a QRCode presented by another //! app that is already connected to the account, which then directs them to -//! a webpage for a simplified signing flow. This is exposed by the -//! [`begin_pairing_flow`](FirefoxAccount::begin_pairing_flow) method. +//! a webpage for a simplified signing flow. This is driven by the +//! [`FxaEvent::BeginPairingFlow`] event. //! //! Technical details of the pairing flow can be found in the [Firefox Accounts //! documentation hub](https://mozilla.github.io/ecosystem-platform/docs/features/firefox-accounts/pairing). @@ -62,109 +62,17 @@ impl FirefoxAccount { self.internal.lock().handle_web_channel_login(&json_payload) } - /// Initiate a web-based OAuth sign-in flow. - /// - /// This method initializes some internal state and then returns a URL at which the - /// user may perform a web-based authorization flow to connect the application to - /// their account. The application should direct the user to the provided URL. - /// - /// When the resulting OAuth flow redirects back to the configured `redirect_uri`, - /// the query parameters should be extracting from the URL and passed to the - /// [`complete_oauth_flow`](FirefoxAccount::complete_oauth_flow) method to finalize - /// the signin. - /// - /// # Arguments - /// - /// - `scopes` - list of OAuth scopes to request. - /// - The requested scopes will determine what account-related data - /// the application is able to access. - /// - `entrypoint` - metrics identifier for UX entrypoint. - /// - This parameter is used for metrics purposes, to identify the - /// UX entrypoint from which the user triggered the signin request. - /// For example, the application toolbar, on the onboarding flow. - /// - `metrics` - optionally, additional metrics tracking parameters. - /// - These will be included as query parameters in the resulting URL. - #[handle_error(Error)] - pub fn begin_oauth_flow>( - &self, - // Allow both &[String] and &[&str] since UniFFI can't represent `&[&str]` yet, - scopes: &[T], - entrypoint: &str, - service: &str, - ) -> ApiResult { - let scopes = scopes.iter().map(T::as_ref).collect::>(); - self.internal - .lock() - .begin_oauth_flow(service, &scopes, entrypoint) - } - /// Get the URL at which to begin a device-pairing signin flow. /// /// If the user wants to sign in using device pairing, call this method and then /// direct them to visit the resulting URL on an already-signed-in device. Doing /// so will trigger the other device to show a QR code to be scanned, and the result - /// from said QR code can be passed to [`begin_pairing_flow`](FirefoxAccount::begin_pairing_flow). + /// from said QR code can be passed to the [`FxaEvent::BeginPairingFlow`] event. #[handle_error(Error)] pub fn get_pairing_authority_url(&self) -> ApiResult { self.internal.lock().get_pairing_authority_url() } - /// Initiate a device-pairing sign-in flow. - /// - /// Once the user has scanned a pairing QR code, pass the scanned value to this - /// method. It will return a URL to which the application should redirect the user - /// in order to continue the sign-in flow. - /// - /// When the resulting flow redirects back to the configured `redirect_uri`, - /// the resulting OAuth parameters should be extracting from the URL and passed - /// to [`complete_oauth_flow`](FirefoxAccount::complete_oauth_flow) to finalize - /// the signin. - /// - /// # Arguments - /// - /// - `pairing_url` - the URL scanned from a QR code on another device. - /// - `scopes` - list of OAuth scopes to request. - /// - The requested scopes will determine what account-related data - /// the application is able to access. - /// - `entrypoint` - metrics identifier for UX entrypoint. - /// - This parameter is used for metrics purposes, to identify the - /// UX entrypoint from which the user triggered the signin request. - /// For example, the application toolbar, on the onboarding flow. - /// - `metrics` - optionally, additional metrics tracking parameters. - /// - These will be included as query parameters in the resulting URL. - #[handle_error(Error)] - pub fn begin_pairing_flow( - &self, - pairing_url: &str, - scopes: &[String], - entrypoint: &str, - service: &str, - ) -> ApiResult { - // UniFFI can't represent `&[&str]` yet, so convert it internally here. - let scopes = scopes.iter().map(String::as_str).collect::>(); - self.internal - .lock() - .begin_pairing_flow(pairing_url, service, &scopes, entrypoint) - } - - /// Complete an OAuth flow. - /// - /// **💾 This method alters the persisted account state.** - /// - /// At the conclusion of an OAuth flow, the user will be redirect to the - /// application's registered `redirect_uri`. It should extract the `code` - /// and `state` parameters from the resulting URL and pass them to this - /// method in order to complete the sign-in. - /// - /// # Arguments - /// - /// - `code` - the OAuth authorization code obtained from the redirect URI. - /// - `state` - the OAuth state parameter obtained from the redirect URI. - #[handle_error(Error)] - pub fn complete_oauth_flow(&self, code: &str, state: &str) -> ApiResult<()> { - self.internal.lock().complete_oauth_flow(code, state) - } - /// Check authorization status for this application. /// /// **💾 This method alters the persisted account state.** diff --git a/components/fxa-client/src/fxa_client.udl b/components/fxa-client/src/fxa_client.udl index feabe98af3..115907ea6d 100644 --- a/components/fxa-client/src/fxa_client.udl +++ b/components/fxa-client/src/fxa_client.udl @@ -21,10 +21,9 @@ typedef enum DeviceType; /// calling [`FirefoxAccount::from_json`]. /// /// * When the user wants to sign in to your application, direct them through -/// a web-based OAuth flow using [`begin_oauth_flow`](FirefoxAccount::begin_oauth_flow) -/// or [`begin_pairing_flow`](FirefoxAccount::begin_pairing_flow); when they return -/// to your registered `redirect_uri`, pass the resulting authorization state back to -/// [`complete_oauth_flow`](FirefoxAccount::complete_oauth_flow) to sign them in. +/// a web-based OAuth flow by sending the `BeginOAuthFlow` or `BeginPairingFlow` +/// state-machine event; when they return to your registered `redirect_uri`, pass the +/// resulting authorization state back via the `CompleteOAuthFlow` event to sign them in. /// /// * Display information about the signed-in user by using the data from /// [`get_profile`](FirefoxAccount::get_profile). @@ -188,86 +187,17 @@ interface FirefoxAccount { /// state. Returns null if no session token is set. string? get_signed_in_user_for_web_channel(); - /// Initiate a web-based OAuth sign-in flow. - /// - /// This method initializes some internal state and then returns a URL at which the - /// user may perform a web-based authorization flow to connect the application to - /// their account. The application should direct the user to the provided URL. - /// - /// When the resulting OAuth flow redirects back to the configured `redirect_uri`, - /// the query parameters should be extracting from the URL and passed to the - /// [`complete_oauth_flow`](FirefoxAccount::complete_oauth_flow) method to finalize - /// the signin. - /// - /// # Arguments - /// - /// - `scopes` - list of OAuth scopes to request. - /// - The requested scopes will determine what account-related data - /// the application is able to access. - /// - `entrypoint` - metrics identifier for UX entrypoint. - /// - This parameter is used for metrics purposes, to identify the - /// UX entrypoint from which the user triggered the signin request. - /// For example, the application toolbar, on the onboarding flow. - /// - `service` - The service being signed up for. - // - The combination of service and entrypoint may impact the UI shown. - [Throws=FxaError] - string begin_oauth_flow([ByRef] sequence scopes, [ByRef] string entrypoint, [ByRef] optional string service = ""); - /// Get the URL at which to begin a device-pairing signin flow. /// /// If the user wants to sign in using device pairing, call this method and then /// direct them to visit the resulting URL on an already-signed-in device. Doing /// so will trigger the other device to show a QR code to be scanned, and the result - /// from said QR code can be passed to [`begin_pairing_flow`](FirefoxAccount::begin_pairing_flow). + /// from said QR code can be passed to the `BeginPairingFlow` state-machine event. /// [Throws=FxaError] string get_pairing_authority_url(); - - /// Initiate a device-pairing sign-in flow. - /// - /// Once the user has scanned a pairing QR code, pass the scanned value to this - /// method. It will return a URL to which the application should redirect the user - /// in order to continue the sign-in flow. - /// - /// When the resulting flow redirects back to the configured `redirect_uri`, - /// the resulting OAuth parameters should be extracting from the URL and passed - /// to [`complete_oauth_flow`](FirefoxAccount::complete_oauth_flow) to finalize - /// the signin. - /// - /// # Arguments - /// - /// - `pairing_url` - the URL scanned from a QR code on another device. - /// - `scopes` - list of OAuth scopes to request. - /// - The requested scopes will determine what account-related data - /// the application is able to access. - /// - `entrypoint` - metrics identifier for UX entrypoint. - /// - This parameter is used for metrics purposes, to identify the - /// UX entrypoint from which the user triggered the signin request. - /// For example, the application toolbar, on the onboarding flow. - /// - `service` - The service being signed up for. - // - The combination of service and entrypoint may impact the UI shown. - [Throws=FxaError] - string begin_pairing_flow([ByRef] string pairing_url, [ByRef] sequence scopes, [ByRef] string entrypoint, [ByRef] optional string service = ""); - - /// Complete an OAuth flow. - /// - /// **💾 This method alters the persisted account state.** - /// - /// At the conclusion of an OAuth flow, the user will be redirect to the - /// application's registered `redirect_uri`. It should extract the `code` - /// and `state` parameters from the resulting URL and pass them to this - /// method in order to complete the sign-in. - /// - /// # Arguments - /// - /// - `code` - the OAuth authorization code obtained from the redirect URI. - /// - `state` - the OAuth state parameter obtained from the redirect URI. - /// - [Throws=FxaError] - void complete_oauth_flow([ByRef] string code, [ByRef] string state ); - /// Check authorization status for this application. /// /// **💾 This method alters the persisted account state.** diff --git a/components/fxa-client/src/lib.rs b/components/fxa-client/src/lib.rs index 40e2fdd9f6..2b19e8fd7a 100644 --- a/components/fxa-client/src/lib.rs +++ b/components/fxa-client/src/lib.rs @@ -22,10 +22,10 @@ //! calling [`FirefoxAccount::from_json`]. //! //! * When the user wants to sign in to your application, direct them through -//! a web-based OAuth flow using [`begin_oauth_flow`](FirefoxAccount::begin_oauth_flow) -//! or [`begin_pairing_flow`](FirefoxAccount::begin_pairing_flow); when they return -//! to your registered `redirect_uri`, pass the resulting authorization state back to -//! [`complete_oauth_flow`](FirefoxAccount::complete_oauth_flow) to sign them in. +//! a web-based OAuth flow by sending the [`FxaEvent::BeginOAuthFlow`] or +//! [`FxaEvent::BeginPairingFlow`] event to [`FirefoxAccount::process_event`]; when they +//! return to your registered `redirect_uri`, pass the resulting authorization state back +//! via the [`FxaEvent::CompleteOAuthFlow`] event to sign them in. //! //! * Display information about the signed-in user by using the data from //! [`get_profile`](FirefoxAccount::get_profile).