Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
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<String>,
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
Expand Down Expand Up @@ -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).
Expand Down Expand Up @@ -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.
*/
Expand Down
106 changes: 7 additions & 99 deletions components/fxa-client/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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<T: AsRef<str>>(
&self,
// Allow both &[String] and &[&str] since UniFFI can't represent `&[&str]` yet,
scopes: &[T],
entrypoint: &str,
service: &str,
) -> ApiResult<String> {
let scopes = scopes.iter().map(T::as_ref).collect::<Vec<_>>();
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<String> {
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<String> {
// UniFFI can't represent `&[&str]` yet, so convert it internally here.
let scopes = scopes.iter().map(String::as_str).collect::<Vec<_>>();
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.**
Expand Down
78 changes: 4 additions & 74 deletions components/fxa-client/src/fxa_client.udl
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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<string> 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<string> 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.**
Expand Down
8 changes: 4 additions & 4 deletions components/fxa-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down