Skip to main content

outlayer_cli/commands/
auth.rs

1use anyhow::{Context, Result};
2
3use crate::api::ApiClient;
4use crate::config::{self, Credentials, NetworkConfig};
5
6/// `outlayer login [network]` — import full access key
7pub async fn login(network_name: &str) -> Result<()> {
8    let network = config::resolve_network(Some(network_name), None)?;
9
10    // Prompt account_id
11    eprint!("Account ID: ");
12    let mut input = String::new();
13    std::io::stdin().read_line(&mut input)?;
14    let account_id = input.trim().to_string();
15    if account_id.is_empty() {
16        anyhow::bail!("Account ID is required");
17    }
18
19    // Prompt private key
20    eprint!("Private key (ed25519:...): ");
21    let mut input = String::new();
22    std::io::stdin().read_line(&mut input)?;
23    let secret_key = input.trim().to_string();
24    if secret_key.is_empty() {
25        anyhow::bail!("Private key is required");
26    }
27
28    // Validate key format
29    let parsed: near_crypto::SecretKey = secret_key
30        .parse()
31        .context("Invalid private key format. Expected: ed25519:...")?;
32    let public_key = parsed.public_key().to_string();
33
34    // Save credentials (always save key to file; keyring is a bonus)
35    let _use_keyring = config::save_private_key(&network.network_id, &account_id, &secret_key);
36
37    let creds = Credentials {
38        account_id: account_id.clone(),
39        public_key: public_key.clone(),
40        private_key: Some(secret_key),
41        contract_id: network.contract_id.clone(),
42        auth_type: "near_key".to_string(),
43        wallet_key: None,
44    };
45
46    config::save_credentials(&network, &creds)?;
47    config::save_default_network(&network.network_id);
48
49    eprintln!("Logged in as {account_id} ({network_name})");
50    eprintln!("Public key: {public_key}");
51
52    Ok(())
53}
54
55/// `outlayer login --wallet-key wk_...` — login with custody wallet API key
56pub async fn login_wallet_key(network_name: &str, wallet_key: &str) -> Result<()> {
57    if !wallet_key.starts_with("wk_") {
58        anyhow::bail!("Invalid wallet key format. Expected: wk_...");
59    }
60
61    let network = config::resolve_network(Some(network_name), None)?;
62    let api = ApiClient::new(&network);
63
64    eprintln!("Authenticating with wallet key...");
65
66    // Sign a message to derive account_id and public_key
67    let resp = api
68        .sign_message(wallet_key, "outlayer-cli-login", &network.contract_id, None)
69        .await
70        .context("Failed to authenticate wallet key. Check that the key is valid.")?;
71
72    let creds = Credentials {
73        account_id: resp.account_id.clone(),
74        public_key: resp.public_key.clone(),
75        private_key: None,
76        contract_id: network.contract_id.clone(),
77        auth_type: "wallet_key".to_string(),
78        wallet_key: Some(wallet_key.to_string()),
79    };
80
81    config::save_credentials(&network, &creds)?;
82    config::save_default_network(&network.network_id);
83
84    eprintln!("Logged in as {} ({network_name})", resp.account_id);
85    eprintln!("Public key: {}", resp.public_key);
86    eprintln!("Auth: wallet_key");
87
88    Ok(())
89}
90
91/// `outlayer logout` — delete stored credentials
92pub fn logout(network: &NetworkConfig) -> Result<()> {
93    config::delete_credentials(network)?;
94    eprintln!("Logged out from {}", network.network_id);
95    Ok(())
96}
97
98/// `outlayer whoami` — show current account
99pub fn whoami(network: &NetworkConfig) -> Result<()> {
100    let creds = config::load_credentials(network)?;
101    println!("Account:  {}", creds.account_id);
102    println!("Network:  {}", network.network_id);
103    println!("Contract: {}", creds.contract_id);
104    println!("Key:      {}", creds.public_key);
105    println!("Auth:     {}", creds.auth_type);
106    Ok(())
107}