greppy/auth/
storage.rs

1use anyhow::{Context, Result};
2use keyring::Entry;
3
4const SERVICE_NAME: &str = "greppy";
5
6/// Provider-specific user names for token storage
7const GOOGLE_USER: &str = "google_oauth_token";
8const ANTHROPIC_USER: &str = "anthropic_oauth_token";
9// Legacy key for backwards compatibility
10const LEGACY_USER: &str = "oauth_token";
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Provider {
14    Google,
15    Anthropic,
16}
17
18impl Provider {
19    fn storage_key(&self) -> &'static str {
20        match self {
21            Provider::Google => GOOGLE_USER,
22            Provider::Anthropic => ANTHROPIC_USER,
23        }
24    }
25}
26
27pub fn save_token(provider: Provider, token: &str) -> Result<()> {
28    let entry = Entry::new(SERVICE_NAME, provider.storage_key())?;
29    entry.set_password(token)?;
30    Ok(())
31}
32
33pub fn load_token(provider: Provider) -> Result<String> {
34    let entry = Entry::new(SERVICE_NAME, provider.storage_key())?;
35    entry.get_password().context(format!(
36        "No {:?} auth token found. Please run 'greppy login'.",
37        provider
38    ))
39}
40
41pub fn delete_token(provider: Provider) -> Result<()> {
42    let entry = Entry::new(SERVICE_NAME, provider.storage_key())?;
43    match entry.delete_password() {
44        Ok(_) => Ok(()),
45        Err(keyring::Error::NoEntry) => Ok(()), // Already deleted
46        Err(e) => Err(e.into()),
47    }
48}
49
50/// Check if a token exists for a provider
51pub fn has_token(provider: Provider) -> bool {
52    load_token(provider).is_ok()
53}
54
55/// Delete all tokens (logout from all providers)
56pub fn delete_all_tokens() -> Result<()> {
57    let _ = delete_token(Provider::Google);
58    let _ = delete_token(Provider::Anthropic);
59    // Also try to delete legacy token
60    if let Ok(entry) = Entry::new(SERVICE_NAME, LEGACY_USER) {
61        let _ = entry.delete_password();
62    }
63    Ok(())
64}
65
66// Legacy functions for backwards compatibility
67pub fn save_token_legacy(token: &str) -> Result<()> {
68    save_token(Provider::Google, token)
69}
70
71pub fn load_token_legacy() -> Result<String> {
72    // Try Google first, then legacy key
73    load_token(Provider::Google).or_else(|_| {
74        let entry = Entry::new(SERVICE_NAME, LEGACY_USER)?;
75        entry
76            .get_password()
77            .context("No auth token found. Please run 'greppy login'.")
78    })
79}
80
81pub fn delete_token_legacy() -> Result<()> {
82    delete_token(Provider::Google)
83}