Don't try keychain after authentication fails

Previously, we were achieving this by deleting the keychain item, but this can sometimes fail which leads to an infinite loop. Now, we explicitly never try the keychain when reattempting authentication after authentication fails.

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
Nathan Sobo 2022-03-23 12:15:36 -06:00
parent 29892ce5d7
commit ee9ed936e4
6 changed files with 27 additions and 22 deletions

View file

@ -327,7 +327,12 @@ impl ChatPanel {
let rpc = rpc.clone(); let rpc = rpc.clone();
let this = this.clone(); let this = this.clone();
cx.spawn(|mut cx| async move { cx.spawn(|mut cx| async move {
if rpc.authenticate_and_connect(&cx).log_err().await.is_some() { if rpc
.authenticate_and_connect(true, &cx)
.log_err()
.await
.is_some()
{
cx.update(|cx| { cx.update(|cx| {
if let Some(this) = this.upgrade(cx) { if let Some(this) = this.upgrade(cx) {
if this.is_focused(cx) { if this.is_focused(cx) {

View file

@ -55,7 +55,7 @@ action!(Authenticate);
pub fn init(rpc: Arc<Client>, cx: &mut MutableAppContext) { pub fn init(rpc: Arc<Client>, cx: &mut MutableAppContext) {
cx.add_global_action(move |_: &Authenticate, cx| { cx.add_global_action(move |_: &Authenticate, cx| {
let rpc = rpc.clone(); let rpc = rpc.clone();
cx.spawn(|cx| async move { rpc.authenticate_and_connect(&cx).log_err().await }) cx.spawn(|cx| async move { rpc.authenticate_and_connect(true, &cx).log_err().await })
.detach(); .detach();
}); });
} }
@ -302,7 +302,7 @@ impl Client {
state._reconnect_task = Some(cx.spawn(|cx| async move { state._reconnect_task = Some(cx.spawn(|cx| async move {
let mut rng = StdRng::from_entropy(); let mut rng = StdRng::from_entropy();
let mut delay = Duration::from_millis(100); let mut delay = Duration::from_millis(100);
while let Err(error) = this.authenticate_and_connect(&cx).await { while let Err(error) = this.authenticate_and_connect(true, &cx).await {
log::error!("failed to connect {}", error); log::error!("failed to connect {}", error);
this.set_status( this.set_status(
Status::ReconnectionError { Status::ReconnectionError {
@ -547,6 +547,7 @@ impl Client {
#[async_recursion(?Send)] #[async_recursion(?Send)]
pub async fn authenticate_and_connect( pub async fn authenticate_and_connect(
self: &Arc<Self>, self: &Arc<Self>,
try_keychain: bool,
cx: &AsyncAppContext, cx: &AsyncAppContext,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let was_disconnected = match *self.status().borrow() { let was_disconnected = match *self.status().borrow() {
@ -568,23 +569,22 @@ impl Client {
self.set_status(Status::Reauthenticating, cx) self.set_status(Status::Reauthenticating, cx)
} }
let mut used_keychain = false; let mut read_from_keychain = false;
let credentials = self.state.read().credentials.clone(); let mut credentials = self.state.read().credentials.clone();
let credentials = if let Some(credentials) = credentials { if credentials.is_none() && try_keychain {
credentials credentials = read_credentials_from_keychain(cx);
} else if let Some(credentials) = read_credentials_from_keychain(cx) { read_from_keychain = credentials.is_some();
used_keychain = true; }
credentials if credentials.is_none() {
} else { credentials = Some(match self.authenticate(&cx).await {
let credentials = match self.authenticate(&cx).await {
Ok(credentials) => credentials, Ok(credentials) => credentials,
Err(err) => { Err(err) => {
self.set_status(Status::ConnectionError, cx); self.set_status(Status::ConnectionError, cx);
return Err(err); return Err(err);
} }
}; });
credentials }
}; let credentials = credentials.unwrap();
if was_disconnected { if was_disconnected {
self.set_status(Status::Connecting, cx); self.set_status(Status::Connecting, cx);
@ -595,7 +595,7 @@ impl Client {
match self.establish_connection(&credentials, cx).await { match self.establish_connection(&credentials, cx).await {
Ok(conn) => { Ok(conn) => {
self.state.write().credentials = Some(credentials.clone()); self.state.write().credentials = Some(credentials.clone());
if !used_keychain && IMPERSONATE_LOGIN.is_none() { if !read_from_keychain && IMPERSONATE_LOGIN.is_none() {
write_credentials_to_keychain(&credentials, cx).log_err(); write_credentials_to_keychain(&credentials, cx).log_err();
} }
self.set_connection(conn, cx).await; self.set_connection(conn, cx).await;
@ -603,10 +603,10 @@ impl Client {
} }
Err(EstablishConnectionError::Unauthorized) => { Err(EstablishConnectionError::Unauthorized) => {
self.state.write().credentials.take(); self.state.write().credentials.take();
if used_keychain { if read_from_keychain {
cx.platform().delete_credentials(&ZED_SERVER_URL).log_err(); cx.platform().delete_credentials(&ZED_SERVER_URL).log_err();
self.set_status(Status::SignedOut, cx); self.set_status(Status::SignedOut, cx);
self.authenticate_and_connect(cx).await self.authenticate_and_connect(false, cx).await
} else { } else {
self.set_status(Status::ConnectionError, cx); self.set_status(Status::ConnectionError, cx);
Err(EstablishConnectionError::Unauthorized)? Err(EstablishConnectionError::Unauthorized)?

View file

@ -91,7 +91,7 @@ impl FakeServer {
}); });
client client
.authenticate_and_connect(&cx.to_async()) .authenticate_and_connect(false, &cx.to_async())
.await .await
.unwrap(); .unwrap();
server server

View file

@ -377,7 +377,7 @@ impl Project {
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
cx: &mut AsyncAppContext, cx: &mut AsyncAppContext,
) -> Result<ModelHandle<Self>> { ) -> Result<ModelHandle<Self>> {
client.authenticate_and_connect(&cx).await?; client.authenticate_and_connect(true, &cx).await?;
let response = client let response = client
.request(proto::JoinProject { .request(proto::JoinProject {

View file

@ -5021,7 +5021,7 @@ mod tests {
}); });
client client
.authenticate_and_connect(&cx.to_async()) .authenticate_and_connect(false, &cx.to_async())
.await .await
.unwrap(); .unwrap();

View file

@ -82,7 +82,7 @@ fn main() {
let client = client.clone(); let client = client.clone();
|cx| async move { |cx| async move {
if client.has_keychain_credentials(&cx) { if client.has_keychain_credentials(&cx) {
client.authenticate_and_connect(&cx).await?; client.authenticate_and_connect(true, &cx).await?;
} }
Ok::<_, anyhow::Error>(()) Ok::<_, anyhow::Error>(())
} }