Skip to content

Add credentials_url setting for keychain storage#50047

Merged
probably-neb merged 2 commits intozed-industries:mainfrom
parndt:feature/credentials_url
Apr 23, 2026
Merged

Add credentials_url setting for keychain storage#50047
probably-neb merged 2 commits intozed-industries:mainfrom
parndt:feature/credentials_url

Conversation

@parndt
Copy link
Copy Markdown
Contributor

@parndt parndt commented Feb 25, 2026

Added support for credentials_url, falling back to server_url, to be used as the key for storing information inside Keychain Access. This allows two copies of Zed to be used side by side if the other is launched with a custom --user-data-dir.

  • Added a solid test coverage and/or screenshots from doing manual testing
  • Done a self-review taking into account security and performance aspects

Release Notes:

  • Added support for credentials_url, falling back to server_url, to be used as the key for storing information inside Keychain Access.

@cla-bot
Copy link
Copy Markdown

cla-bot Bot commented Feb 25, 2026

We require contributors to sign our Contributor License Agreement, and we don't have @parndt on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'.

@zed-community-bot zed-community-bot Bot added the first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions label Feb 25, 2026
@parndt

This comment was marked as outdated.

@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Feb 25, 2026
@cla-bot
Copy link
Copy Markdown

cla-bot Bot commented Feb 25, 2026

The cla-bot has been summoned, and re-checked this pull request!

Comment thread crates/client/src/client.rs Outdated
Comment on lines +2251 to +2359
#[derive(Default)]
struct SpyCredentialsProvider {
recorded_urls: Mutex<Vec<String>>,
stored: Mutex<HashMap<String, (String, Vec<u8>)>>,
}

impl CredentialsProvider for SpyCredentialsProvider {
fn read_credentials<'a>(
&'a self,
url: &'a str,
_cx: &'a AsyncApp,
) -> Pin<Box<dyn Future<Output = Result<Option<(String, Vec<u8>)>>> + 'a>> {
async move {
self.recorded_urls.lock().push(url.to_string());
Ok(self.stored.lock().get(url).cloned())
}
.boxed_local()
}

fn write_credentials<'a>(
&'a self,
url: &'a str,
username: &'a str,
password: &'a [u8],
_cx: &'a AsyncApp,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
async move {
self.recorded_urls.lock().push(url.to_string());
self.stored
.lock()
.insert(url.to_string(), (username.to_string(), password.to_vec()));
Ok(())
}
.boxed_local()
}

fn delete_credentials<'a>(
&'a self,
url: &'a str,
_cx: &'a AsyncApp,
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a>> {
async move {
self.recorded_urls.lock().push(url.to_string());
self.stored.lock().remove(url);
Ok(())
}
.boxed_local()
}
}

#[gpui::test]
async fn test_credentials_use_configured_url(cx: &mut TestAppContext) {
init_test(cx);
let spy = Arc::new(SpyCredentialsProvider::default());
let provider = ClientCredentialsProvider::with_provider(spy.clone());
let async_cx = cx.to_async();

provider
.write_credentials(42, "test-token".to_string(), &async_cx)
.await
.unwrap();

let expected_url = cx.update(|cx| ClientSettings::get_global(cx).server_url.clone());
let recorded = spy.recorded_urls.lock().clone();
assert_eq!(recorded, vec![expected_url.clone()]);

let credentials = provider.read_credentials(&async_cx).await.unwrap();
assert_eq!(credentials.user_id, 42);
assert_eq!(credentials.access_token, "test-token");

provider.delete_credentials(&async_cx).await.unwrap();
let recorded = spy.recorded_urls.lock().clone();
assert_eq!(
recorded,
vec![expected_url.clone(), expected_url.clone(), expected_url]
);

assert!(provider.read_credentials(&async_cx).await.is_none());
}

#[gpui::test]
async fn test_credentials_are_keyed_by_url(cx: &mut TestAppContext) {
init_test(cx);
let spy = Arc::new(SpyCredentialsProvider::default());
let provider = ClientCredentialsProvider::with_provider(spy.clone());
let async_cx = cx.to_async();

provider
.write_credentials(1, "token-a".to_string(), &async_cx)
.await
.unwrap();

let expected_url = cx.update(|cx| ClientSettings::get_global(cx).server_url.clone());

let other_url = format!("{}/other", expected_url);
spy.stored.lock().insert(
other_url.clone(),
("2".to_string(), b"token-b".to_vec()),
);

let credentials = provider.read_credentials(&async_cx).await.unwrap();
assert_eq!(credentials.user_id, 1);
assert_eq!(credentials.access_token, "token-a");

provider.delete_credentials(&async_cx).await.unwrap();
assert!(provider.read_credentials(&async_cx).await.is_none());

assert!(spy.stored.lock().get(&other_url).is_some());
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think these tests are worth keeping around. They are essentially just checking the if credentials_url { return credentials_url } else { return server_url }

@probably-neb probably-neb marked this pull request as draft April 17, 2026 10:17
@probably-neb
Copy link
Copy Markdown
Collaborator

are you still working on this @parndt?

@parndt
Copy link
Copy Markdown
Contributor Author

parndt commented Apr 19, 2026

are you still working on this @parndt?

Yes, I'm happy to. After the initial review I do intend to make the change to remove the tests.

This falls back to `server_url` if not configured, and is usable as the
key for storing information inside Keychain Access. This allows distinct
copies of Zed to use different keys which allows the use of multiple
profiles.
@parndt parndt force-pushed the feature/credentials_url branch from 329e2b5 to cd63b73 Compare April 22, 2026 01:41
Improve credentials_url doc comment
@parndt parndt force-pushed the feature/credentials_url branch from cd63b73 to 28b6691 Compare April 22, 2026 01:42
@parndt
Copy link
Copy Markdown
Contributor Author

parndt commented Apr 22, 2026

@probably-neb updated, thanks! I took the opportunity to rebase it.

@parndt parndt marked this pull request as ready for review April 22, 2026 02:39
@probably-neb probably-neb enabled auto-merge April 23, 2026 07:59
@probably-neb probably-neb added this pull request to the merge queue Apr 23, 2026
Merged via the queue into zed-industries:main with commit 4b01676 Apr 23, 2026
49 of 51 checks passed
@parndt parndt deleted the feature/credentials_url branch April 27, 2026 23:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants