Updating the fxa-client example

Added flags to:
  - Request the session scope.
  - Control how log messages get printed to the console.

Updated the code to re-authenticated using the existing data rather than
throwing it away and starting from scratch.

These changes make it possible to repro
https://bugzilla.mozilla.org/show_bug.cgi?id=1887071

- run `cargo run -- --log -d devices` with fresh credentials.
  - Any command should do, but I used `devices`
  - The client should ask you to login and paste your credentials
  - You should see the device list printed out
- In a browser session, change your FxA password
- run `cargo run -- --log -d devices` again
  - The client should tell you there was an auth-problem and ask you to
    reauthenticate.
  - After you paste the reauthentication URL you should see an error
    printed out
This commit is contained in:
Ben Dean-Kawamura 2024-03-26 14:03:24 -04:00 коммит произвёл bendk
Родитель e6ccfed09e
Коммит 6c6951cdfe
9 изменённых файлов: 133 добавлений и 36 удалений

69
Cargo.lock сгенерированный
Просмотреть файл

@ -1003,6 +1003,15 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "deranged"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
]
[[package]]
name = "difference"
version = "2.0.0"
@ -1397,6 +1406,8 @@ dependencies = [
"clap 4.2.2",
"cli-support",
"fxa-client",
"log",
"simple_logger",
"sync15",
"url",
"viaduct-reqwest",
@ -2740,6 +2751,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-integer"
version = "0.1.45"
@ -3144,6 +3161,12 @@ dependencies = [
"plotters-backend",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.16"
@ -3788,18 +3811,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.164"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.164"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@ -3896,6 +3919,18 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3"
[[package]]
name = "simple_logger"
version = "4.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1"
dependencies = [
"colored",
"log",
"time",
"windows-sys 0.48.0",
]
[[package]]
name = "siphasher"
version = "0.3.10"
@ -4284,20 +4319,36 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.11"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217"
checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
dependencies = [
"deranged",
"itoa 1.0.9",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-macros"
version = "0.2.4"
name = "time-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tinytemplate"

Просмотреть файл

@ -11,7 +11,7 @@ use autofill::db::{
};
use autofill::encryption::{create_autofill_key, EncryptorDecryptor};
use autofill::error::Error;
use cli_support::fxa_creds::{get_cli_fxa, get_default_fxa_config};
use cli_support::fxa_creds::{get_cli_fxa, get_default_fxa_config, SYNC_SCOPE};
use cli_support::prompt::{prompt_string, prompt_usize};
use interrupt_support::NeverInterrupts; // XXX need a real interruptee!
use std::sync::Arc;
@ -357,7 +357,7 @@ fn run_sync(
wait: u64,
) -> Result<()> {
// XXX - need to add interrupts
let cli_fxa = get_cli_fxa(get_default_fxa_config(), &cred_file)?;
let cli_fxa = get_cli_fxa(get_default_fxa_config(), &cred_file, &[SYNC_SCOPE])?;
if wipe_all {
Sync15StorageClient::new(cli_fxa.client_init.clone())?.wipe_all_remote()?;

Просмотреть файл

@ -24,7 +24,8 @@ use crate::prompt::prompt_string;
// working option.
const CLIENT_ID: &str = "3c49430b43dfba77";
const REDIRECT_URI: &str = "https://accounts.firefox.com/oauth/success/3c49430b43dfba77";
const SYNC_SCOPE: &str = "https://identity.mozilla.com/apps/oldsync";
pub const SYNC_SCOPE: &str = "https://identity.mozilla.com/apps/oldsync";
pub const SESSION_SCOPE: &str = "https://identity.mozilla.com/tokens/session";
fn load_fxa_creds(path: &str) -> Result<FirefoxAccount> {
let mut file = fs::File::open(path)?;
@ -33,20 +34,25 @@ fn load_fxa_creds(path: &str) -> Result<FirefoxAccount> {
Ok(FirefoxAccount::from_json(&s)?)
}
fn load_or_create_fxa_creds(path: &str, cfg: FxaConfig) -> Result<FirefoxAccount> {
fn load_or_create_fxa_creds(path: &str, cfg: FxaConfig, scopes: &[&str]) -> Result<FirefoxAccount> {
load_fxa_creds(path).or_else(|e| {
log::info!(
"Failed to load existing FxA credentials from {:?} (error: {}), launching OAuth flow",
path,
e
);
create_fxa_creds(path, cfg)
create_fxa_creds(path, cfg, scopes)
})
}
fn create_fxa_creds(path: &str, cfg: FxaConfig) -> Result<FirefoxAccount> {
fn create_fxa_creds(path: &str, cfg: FxaConfig, scopes: &[&str]) -> Result<FirefoxAccount> {
let acct = FirefoxAccount::new(cfg);
let oauth_uri = acct.begin_oauth_flow(&[SYNC_SCOPE], "fxa_creds")?;
handle_oauth_flow(path, &acct, scopes)?;
Ok(acct)
}
fn handle_oauth_flow(path: &str, acct: &FirefoxAccount, scopes: &[&str]) -> Result<()> {
let oauth_uri = acct.begin_oauth_flow(scopes, "fxa_creds")?;
if webbrowser::open(oauth_uri.as_ref()).is_err() {
log::warn!("Failed to open a web browser D:");
@ -68,7 +74,7 @@ fn create_fxa_creds(path: &str, cfg: FxaConfig) -> Result<FirefoxAccount> {
let mut file = fs::File::create(path)?;
write!(file, "{}", acct.to_json()?)?;
file.flush()?;
Ok(acct)
Ok(())
}
// Our public functions. It would be awesome if we could somehow integrate
@ -81,9 +87,10 @@ pub fn get_default_fxa_config() -> FxaConfig {
pub fn get_account_and_token(
config: FxaConfig,
cred_file: &str,
scopes: &[&str],
) -> Result<(FirefoxAccount, AccessTokenInfo)> {
// TODO: we should probably set a persist callback on acct?
let mut acct = load_or_create_fxa_creds(cred_file, config.clone())?;
let acct = load_or_create_fxa_creds(cred_file, config.clone(), scopes)?;
// `scope` could be a param, but I can't see it changing.
match acct.get_access_token(SYNC_SCOPE, None) {
Ok(t) => Ok((acct, t)),
@ -91,8 +98,9 @@ pub fn get_account_and_token(
match e {
// We can retry an auth error.
FxaError::Authentication => {
println!("Saw an auth error using stored credentials - recreating them...");
acct = create_fxa_creds(cred_file, config)?;
println!("Saw an auth error using stored credentials - attempting to re-authenticate");
println!("If fails, consider deleting {cred_file} to start from scratch");
handle_oauth_flow(cred_file, &acct, scopes)?;
let token = acct.get_access_token(SYNC_SCOPE, None)?;
Ok((acct, token))
}
@ -102,8 +110,8 @@ pub fn get_account_and_token(
}
}
pub fn get_cli_fxa(config: FxaConfig, cred_file: &str) -> Result<CliFxa> {
let (account, token_info) = match get_account_and_token(config, cred_file) {
pub fn get_cli_fxa(config: FxaConfig, cred_file: &str, scopes: &[&str]) -> Result<CliFxa> {
let (account, token_info) = match get_account_and_token(config, cred_file, scopes) {
Ok(v) => v,
Err(e) => anyhow::bail!("Failed to use saved credentials. {}", e),
};

Просмотреть файл

@ -8,6 +8,8 @@ publish = false
[dependencies]
viaduct-reqwest = { path = "../../components/support/viaduct-reqwest" }
log = "0.4"
simple_logger = "4"
clap = {version = "4.2", features = ["derive"]}
cli-support = { path = "../cli-support" }
fxa-client = { path = "../../components/fxa-client" }

Просмотреть файл

@ -8,7 +8,7 @@ mod send_tab;
use std::fs;
use clap::{Parser, Subcommand, ValueEnum};
use cli_support::fxa_creds::get_cli_fxa;
use cli_support::fxa_creds;
use fxa_client::{FirefoxAccount, FxaConfig, FxaServer};
static CREDENTIALS_PATH: &str = "credentials.json";
@ -24,6 +24,22 @@ struct Cli {
#[arg(value_enum, default_value_t = Server::Release)]
server: Server,
/// Request a session scope
#[clap(long, short, action)]
session_scope: bool,
/// Print out log to the console. The default level is WARN
#[clap(long, short, action)]
log: bool,
/// Set the logging level to INFO
#[clap(long, short, action)]
info: bool,
/// Set the logging level to DEBUG
#[clap(long, short, action)]
debug: bool,
#[command(subcommand)]
command: Command,
}
@ -52,9 +68,24 @@ enum Command {
fn main() -> Result<()> {
let cli = Cli::parse();
viaduct_reqwest::use_reqwest_backend();
if cli.log {
if cli.debug {
simple_logger::init_with_level(log::Level::Debug).unwrap();
} else if cli.info {
simple_logger::init_with_level(log::Level::Info).unwrap();
} else {
simple_logger::init_with_level(log::Level::Warn).unwrap();
}
}
let scopes: &[&str] = if cli.session_scope {
&[fxa_creds::SYNC_SCOPE, fxa_creds::SESSION_SCOPE]
} else {
&[fxa_creds::SYNC_SCOPE]
};
println!();
let account = load_account(&cli)?;
let account = load_account(&cli, scopes)?;
match cli.command {
Command::Devices(args) => devices::run(&account, args),
Command::SendTab(args) => send_tab::run(&account, args),
@ -67,7 +98,7 @@ fn main() -> Result<()> {
Ok(())
}
fn load_account(cli: &Cli) -> Result<FirefoxAccount> {
fn load_account(cli: &Cli, scopes: &[&str]) -> Result<FirefoxAccount> {
let config = FxaConfig {
server: match cli.server {
Server::Release => FxaServer::Release,
@ -80,7 +111,7 @@ fn load_account(cli: &Cli) -> Result<FirefoxAccount> {
client_id: CLIENT_ID.into(),
token_server_url_override: None,
};
get_cli_fxa(config, CREDENTIALS_PATH).map(|cli| cli.account)
fxa_creds::get_cli_fxa(config, CREDENTIALS_PATH, scopes).map(|cli| cli.account)
}
pub fn persist_fxa_state(acct: &FirefoxAccount) -> Result<()> {

Просмотреть файл

@ -4,7 +4,7 @@
#![warn(rust_2018_idioms)]
use cli_support::fxa_creds::{get_cli_fxa, get_default_fxa_config};
use cli_support::fxa_creds::{get_cli_fxa, get_default_fxa_config, SYNC_SCOPE};
use interrupt_support::Interruptee;
use places::storage::bookmarks::{
json_tree::{
@ -237,7 +237,7 @@ fn sync(
) -> Result<()> {
use_reqwest_backend();
let cli_fxa = get_cli_fxa(get_default_fxa_config(), &cred_file)?;
let cli_fxa = get_cli_fxa(get_default_fxa_config(), &cred_file, &[SYNC_SCOPE])?;
if wipe_all {
Sync15StorageClient::new(cli_fxa.client_init.clone())?.wipe_all_remote()?;

Просмотреть файл

@ -6,7 +6,9 @@
#![warn(rust_2018_idioms)]
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use cli_support::fxa_creds::{get_account_and_token, get_cli_fxa, get_default_fxa_config};
use cli_support::fxa_creds::{
get_account_and_token, get_cli_fxa, get_default_fxa_config, SYNC_SCOPE,
};
use cli_support::prompt::{prompt_char, prompt_string, prompt_usize};
use logins::encryption::{create_key, EncryptorDecryptor};
use logins::{
@ -475,12 +477,12 @@ fn main() -> Result<()> {
}
'S' | 's' => {
log::info!("Syncing!");
let (_, token_info) = get_account_and_token(get_default_fxa_config(), cred_file)?;
let (_, token_info) = get_account_and_token(get_default_fxa_config(), cred_file, &[SYNC_SCOPE])?;
let sync_key = URL_SAFE_NO_PAD.encode(
token_info.key.unwrap().key_bytes()?,
);
// TODO: allow users to use stage/etc.
let cli_fxa = get_cli_fxa(get_default_fxa_config(), cred_file)?;
let cli_fxa = get_cli_fxa(get_default_fxa_config(), cred_file, &[SYNC_SCOPE])?;
match do_sync(
Arc::clone(&store),
cli_fxa.client_init.key_id.clone(),

Просмотреть файл

@ -5,7 +5,9 @@
#![warn(rust_2018_idioms)]
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use cli_support::fxa_creds::{get_account_and_token, get_cli_fxa, get_default_fxa_config};
use cli_support::fxa_creds::{
get_account_and_token, get_cli_fxa, get_default_fxa_config, SYNC_SCOPE,
};
use cli_support::prompt::{prompt_char, prompt_string};
use interrupt_support::NeverInterrupts;
use std::path::Path;
@ -100,10 +102,11 @@ fn main() -> Result<()> {
cli_support::init_logging();
let opts = Opts::from_args();
let (_, token_info) = get_account_and_token(get_default_fxa_config(), &opts.creds_file)?;
let (_, token_info) =
get_account_and_token(get_default_fxa_config(), &opts.creds_file, &[SYNC_SCOPE])?;
let sync_key = URL_SAFE_NO_PAD.encode(token_info.key.unwrap().key_bytes()?);
let cli_fxa = get_cli_fxa(get_default_fxa_config(), &opts.creds_file)?;
let cli_fxa = get_cli_fxa(get_default_fxa_config(), &opts.creds_file, &[SYNC_SCOPE])?;
let device_id = cli_fxa.account.get_current_device_id()?;
let store = Arc::new(TabsStore::new(Path::new(&opts.db_path)));

Просмотреть файл

@ -4,7 +4,7 @@ http://creativecommons.org/publicdomain/zero/1.0/ */
#![allow(unknown_lints)]
#![warn(rust_2018_idioms)]
use cli_support::fxa_creds::{get_cli_fxa, get_default_fxa_config};
use cli_support::fxa_creds::{get_cli_fxa, get_default_fxa_config, SYNC_SCOPE};
use std::{collections::HashSet, process};
use std::sync::Arc;
use structopt::StructOpt;
@ -78,7 +78,7 @@ pub fn run_test_group(opts: &Opts, group: TestGroup) {
}
let cfg = get_default_fxa_config();
let cli_fxa = get_cli_fxa(cfg, &opts.credential_file).expect("can't initialize cli");
let cli_fxa = get_cli_fxa(cfg, &opts.credential_file, &[SYNC_SCOPE]).expect("can't initialize cli");
let acct = Arc::new(cli_fxa);
let mut user = TestUser::new(acct, 2).expect("Failed to get test user.");