Show organization name after authenticating (#15823)

## Summary

Shows the name of the logged-in organization on success, rather than
repeating the URL.
This commit is contained in:
Charlie Marsh 2025-09-16 09:46:43 -04:00 committed by GitHub
parent 663053b0d1
commit ac52201626
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 32 additions and 17 deletions

View File

@ -10,7 +10,9 @@ pub use credentials::{Credentials, Username};
pub use index::{AuthPolicy, Index, Indexes};
pub use keyring::KeyringProvider;
pub use middleware::AuthMiddleware;
pub use pyx::{DEFAULT_TOLERANCE_SECS, PyxOAuthTokens, PyxTokenStore, PyxTokens, TokenStoreError};
pub use pyx::{
DEFAULT_TOLERANCE_SECS, PyxJwt, PyxOAuthTokens, PyxTokenStore, PyxTokens, TokenStoreError,
};
pub use realm::Realm;
pub use service::{Service, ServiceParseError};
pub use store::{AuthBackend, AuthScheme, TextCredentialStore, TomlCredentialError};

View File

@ -368,9 +368,9 @@ impl PyxTokenStore {
tolerance_secs: u64,
) -> Result<PyxTokens, TokenStoreError> {
// Decode the access token.
let jwt = Jwt::decode(match &tokens {
PyxTokens::OAuth(PyxOAuthTokens { access_token, .. }) => access_token.as_str(),
PyxTokens::ApiKey(PyxApiKeyTokens { access_token, .. }) => access_token.as_str(),
let jwt = PyxJwt::decode(match &tokens {
PyxTokens::OAuth(PyxOAuthTokens { access_token, .. }) => access_token,
PyxTokens::ApiKey(PyxApiKeyTokens { access_token, .. }) => access_token,
})?;
// If the access token is expired, refresh it.
@ -503,14 +503,20 @@ impl TokenStoreError {
/// The payload of the JWT.
#[derive(Debug, serde::Deserialize)]
struct Jwt {
exp: Option<i64>,
pub struct PyxJwt {
/// The expiration time of the JWT, as a Unix timestamp.
pub exp: Option<i64>,
/// The issuer of the JWT.
pub iss: Option<String>,
/// The name of the organization, if any.
#[serde(rename = "urn:pyx:org_name")]
pub name: Option<String>,
}
impl Jwt {
impl PyxJwt {
/// Decode the JWT from the access token.
fn decode(access_token: &str) -> Result<Self, JwtError> {
let mut token_segments = access_token.splitn(3, '.');
pub fn decode(access_token: &AccessToken) -> Result<Self, JwtError> {
let mut token_segments = access_token.as_str().splitn(3, '.');
let _header = token_segments.next().ok_or(JwtError::MissingHeader)?;
let payload = token_segments.next().ok_or(JwtError::MissingPayload)?;

View File

@ -7,8 +7,8 @@ use url::Url;
use uuid::Uuid;
use uv_auth::{
AccessToken, AuthBackend, Credentials, PyxOAuthTokens, PyxTokenStore, PyxTokens, Service,
TextCredentialStore,
AccessToken, AuthBackend, Credentials, PyxJwt, PyxOAuthTokens, PyxTokenStore, PyxTokens,
Service, TextCredentialStore,
};
use uv_client::{AuthIntegration, BaseClient, BaseClientBuilder};
use uv_distribution_types::IndexUrl;
@ -45,12 +45,19 @@ pub(crate) async fn login(
.auth_integration(AuthIntegration::NoAuthMiddleware)
.build();
pyx_login_with_browser(&pyx_store, &client, &printer).await?;
writeln!(
printer.stderr(),
"Logged in to {}",
pyx_store.api().bold().cyan()
)?;
let access_token = pyx_login_with_browser(&pyx_store, &client, &printer).await?;
let jwt = PyxJwt::decode(&access_token)?;
if let Some(name) = jwt.name.as_deref() {
writeln!(printer.stderr(), "Logged in to {}", name.bold().cyan())?;
} else {
writeln!(
printer.stderr(),
"Logged in to {}",
pyx_store.api().bold().cyan()
)?;
}
return Ok(ExitStatus::Success);
}