Skip to content

Conversation

@cmwhited
Copy link
Contributor

Description

Updated the auth flow in ampcc to enable authenticating using the PKCE device flow against the platform auth ui.

Auth state/flow:

  1. check if the user passed in an AMP_AUTH_TOKEN to the env
  2. check {home_dir}/.amp/cache/amp_cli_auth, which is set by the amp cli (and now this TUI as well)
  3. give users the ability to authenticate using the PKCE device flow with the platform auth ui
  4. give users the ability to logout
  5. show the users connected wallet in the toolbar. may update this to be more dynamic based on how they logged in (discord, email, etc)
CleanShot.2026-01-20.at.12.24.04.mp4

@shiyasmohd shiyasmohd self-requested a review January 21, 2026 17:08

/// Errors that can occur during authentication operations.
#[derive(Debug, Error)]
pub enum AuthError {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add detailed docs for each errors?

Comment on lines +80 to +94
/// 1. AMP_AUTH_TOKEN environment variable (highest priority)
/// 2. Auth storage file (~/.amp/cache/amp_cli_auth)
pub fn new(base_url: String) -> Self {
let auth_token = Self::load_auth_token();
// Use Client::new() like other working clients in the codebase
let http = reqwest::Client::new();

Self {
http,
base_url: base_url.trim_end_matches('/').to_string(),
auth_token,
}
// Load from auth storage file, then pass to with_token which checks env var
let file_token = AuthStorage::load().map(|a| a.access_token);
Self::with_token(base_url, file_token)
}

/// Create a new registry client with a specific auth token.
#[allow(dead_code)]
pub fn with_auth(base_url: String, token: String) -> Self {
/// Create a new registry client with an explicit auth token.
///
/// If `token` is `None`, falls back to AMP_AUTH_TOKEN environment variable.
/// Use this when the app's auth state changes (login, logout, refresh).
pub fn with_token(base_url: String, token: Option<String>) -> Self {
// Use explicit token if provided, otherwise fall back to env var
let auth_token = token.or_else(Self::load_auth_from_env);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs for new() says, env has highest priority, but its actually used as fallback.

Copy link
Contributor

@shiyasmohd shiyasmohd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation looks nice, and works smooth. Some changes needed

  • Added in comments
  • Can we remove the merge commit and rebase the branch?
  • There is a lag when selecting pane using mouse click, that needs to be fixed

Comment on lines +30 to +32
pub fn is_evm_address(s: &str) -> bool {
s.starts_with("0x") && s.parse::<Address>().is_ok()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be a simple validation fn instead of importing alloy crate.
s.len() == 42 && s.starts_with("0x") && s[2..].chars().all(|c| c.is_ascii_hexdigit())

Action::SwitchToLocal => {
if self.current_source != DataSource::Local {
self.start_loading("Switching source...");
// Spawn task to switch source
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment could be changed to something like, Async spawning handled in handle_async_action

Comment on lines +1079 to +1080
// Spawn task to switch source
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

// Datasets
Action::RefreshDatasets => {
self.start_loading("Refreshing datasets...");
// Spawn task to refresh datasets
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update comment

// Manifest
Action::LoadManifest => {
self.start_loading("Loading manifest...");
// Spawn task to load manifest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update comment


Action::LoadWorkerDetail(_node_id) => {
self.start_loading("Loading worker details...");
// Spawn task to load worker detail
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update comment

Action::AuthLogin => {
if self.auth_state.is_none() && self.auth_device_flow.is_none() {
self.start_loading("Logging in...");
// Spawn task to start device flow
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update comment

Comment on lines +1292 to +1293
let _ = clipboard.set_text(&user_code);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could silently ignore copy to clipboard errors and showing users copied to clipboard in auth_screen.rs, could be fixed in another PR.

Comment on lines +1322 to +1333
Action::AuthDeviceFlowPoll {
device_code,
code_verifier,
interval,
is_first_poll,
} => {
if let Some(ref mut flow) = self.auth_device_flow {
flow.status = DeviceFlowStatus::Polling;
}
// Spawn task to poll for token
let _ = (device_code, code_verifier, interval, is_first_poll); // Will be used by spawn
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Action::AuthDeviceFlowPoll {
device_code,
code_verifier,
interval,
is_first_poll,
} => {
if let Some(ref mut flow) = self.auth_device_flow {
flow.status = DeviceFlowStatus::Polling;
}
// Spawn task to poll for token
let _ = (device_code, code_verifier, interval, is_first_poll); // Will be used by spawn
}
Action::AuthDeviceFlowPoll { .. } => {
if let Some(ref mut flow) = self.auth_device_flow {
flow.status = DeviceFlowStatus::Polling;
}
// Updated comment with same pattern as others
}


Action::AuthDeviceFlowConfirm => {
if let Some(ref mut flow) = self.auth_device_flow {
let _ = crate::auth::PkceDeviceFlowClient::open_browser(&flow.verification_uri);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should handle the case, if opening browser fails, maybe like show url in terminal, but we don't support selecting text in tui since we have selecting pane using mouse pointer feature. this needs a discussion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants