Don't trigger authentication flow unless credentials expired (#35570)

This fixes a regression introduced in
https://github.com/zed-industries/zed/pull/35471, where we treated
stored credentials as invalid when failing to retrieve the authenticated
user for any reason. This had the side effect of triggering the auth
flow even when e.g. the client/server had temporary networking issues.

This pull request changes the logic to only trigger authentication when
getting a 401 from the server.

Release Notes:

- N/A
This commit is contained in:
Antonio Scandurra 2025-08-04 10:41:23 +02:00 committed by GitHub
parent 5ca5d90234
commit 7217439c97
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 137 additions and 34 deletions

View file

@ -1,10 +1,10 @@
use std::sync::Arc;
use anyhow::{Result, anyhow};
use anyhow::{Context, Result, anyhow};
pub use cloud_api_types::*;
use futures::AsyncReadExt as _;
use http_client::http::request;
use http_client::{AsyncBody, HttpClientWithUrl, Method, Request};
use http_client::{AsyncBody, HttpClientWithUrl, Method, Request, StatusCode};
use parking_lot::RwLock;
struct Credentials {
@ -40,27 +40,14 @@ impl CloudApiClient {
*self.credentials.write() = None;
}
fn authorization_header(&self) -> Result<String> {
let guard = self.credentials.read();
let credentials = guard
.as_ref()
.ok_or_else(|| anyhow!("No credentials provided"))?;
Ok(format!(
"{} {}",
credentials.user_id, credentials.access_token
))
}
fn build_request(
&self,
req: request::Builder,
body: impl Into<AsyncBody>,
) -> Result<Request<AsyncBody>> {
Ok(req
.header("Content-Type", "application/json")
.header("Authorization", self.authorization_header()?)
.body(body.into())?)
let credentials = self.credentials.read();
let credentials = credentials.as_ref().context("no credentials provided")?;
build_request(req, body, credentials)
}
pub async fn get_authenticated_user(&self) -> Result<GetAuthenticatedUserResponse> {
@ -152,4 +139,50 @@ impl CloudApiClient {
Ok(serde_json::from_str(&body)?)
}
pub async fn validate_credentials(&self, user_id: u32, access_token: &str) -> Result<bool> {
let request = build_request(
Request::builder().method(Method::GET).uri(
self.http_client
.build_zed_cloud_url("/client/users/me", &[])?
.as_ref(),
),
AsyncBody::default(),
&Credentials {
user_id,
access_token: access_token.into(),
},
)?;
let mut response = self.http_client.send(request).await?;
if response.status().is_success() {
Ok(true)
} else {
let mut body = String::new();
response.body_mut().read_to_string(&mut body).await?;
if response.status() == StatusCode::UNAUTHORIZED {
return Ok(false);
} else {
return Err(anyhow!(
"Failed to get authenticated user.\nStatus: {:?}\nBody: {body}",
response.status()
));
}
}
}
}
fn build_request(
req: request::Builder,
body: impl Into<AsyncBody>,
credentials: &Credentials,
) -> Result<Request<AsyncBody>> {
Ok(req
.header("Content-Type", "application/json")
.header(
"Authorization",
format!("{} {}", credentials.user_id, credentials.access_token),
)
.body(body.into())?)
}