From 26a4267b882200facbea8651219699f8c6222424 Mon Sep 17 00:00:00 2001 From: Anshul Garg Date: Wed, 11 Mar 2026 22:45:19 +0530 Subject: [PATCH] fix(auth): map People service to contacts/directory scope prefixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The People API exposes scopes like `contacts`, `contacts.readonly`, and `directory.readonly`, none of which start with `people`. When users ran `gws auth login -s people`, zero scopes matched because `map_service_to_scope_prefix` returned `"people"` verbatim. Change `map_service_to_scope_prefix` to `map_service_to_scope_prefixes` returning a Vec to support services that map to multiple scope prefixes. Add the `people` → `["contacts", "directory"]` mapping. Chat scopes (chat.spaces, chat.messages) already matched correctly since they share the `chat` prefix, but this is now verified by tests. Closes #310 Closes #316 --- .changeset/fix-people-chat-scope-mapping.md | 5 ++ src/auth_commands.rs | 60 +++++++++++++++++---- 2 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 .changeset/fix-people-chat-scope-mapping.md diff --git a/.changeset/fix-people-chat-scope-mapping.md b/.changeset/fix-people-chat-scope-mapping.md new file mode 100644 index 00000000..6fca6727 --- /dev/null +++ b/.changeset/fix-people-chat-scope-mapping.md @@ -0,0 +1,5 @@ +--- +"@googleworkspace/cli": patch +--- + +Map People service to `contacts` and `directory` scope prefixes so `gws auth login -s people` includes the required OAuth scopes diff --git a/src/auth_commands.rs b/src/auth_commands.rs index ade681d6..587cdc29 100644 --- a/src/auth_commands.rs +++ b/src/auth_commands.rs @@ -549,18 +549,23 @@ fn scope_matches_service(scope_url: &str, services: &HashSet) -> bool { let prefix = short.split('.').next().unwrap_or(short); services.iter().any(|svc| { - let mapped_svc = map_service_to_scope_prefix(svc); - prefix == mapped_svc || short.starts_with(&format!("{mapped_svc}.")) + let prefixes = map_service_to_scope_prefixes(svc); + prefixes + .iter() + .any(|mapped| prefix == *mapped || short.starts_with(&format!("{mapped}."))) }) } /// Map user-friendly service names to their OAuth scope prefixes. -fn map_service_to_scope_prefix(service: &str) -> &str { +/// Some services map to multiple scope prefixes (e.g. People API uses +/// both `contacts` and `directory` scopes). +fn map_service_to_scope_prefixes(service: &str) -> Vec<&str> { match service { - "sheets" => "spreadsheets", - "slides" => "presentations", - "docs" => "documents", - s => s, + "sheets" => vec!["spreadsheets"], + "slides" => vec!["presentations"], + "docs" => vec!["documents"], + "people" => vec!["contacts", "directory"], + s => vec![s], } } @@ -1344,8 +1349,11 @@ fn find_unmatched_services(scopes: &[String], services: &HashSet) -> Has if matched_services.contains(service) { continue; } - let mapped_svc = map_service_to_scope_prefix(service); - if prefix == mapped_svc || short.starts_with(&format!("{mapped_svc}.")) { + let prefixes = map_service_to_scope_prefixes(service); + if prefixes + .iter() + .any(|mapped| prefix == *mapped || short.starts_with(&format!("{mapped}."))) + { matched_services.insert(service.clone()); } } @@ -1926,6 +1934,40 @@ mod tests { )); } + #[test] + fn scope_matches_service_people_contacts() { + let services: HashSet = ["people"].iter().map(|s| s.to_string()).collect(); + assert!(scope_matches_service( + "https://www.googleapis.com/auth/contacts", + &services + )); + assert!(scope_matches_service( + "https://www.googleapis.com/auth/contacts.readonly", + &services + )); + assert!(scope_matches_service( + "https://www.googleapis.com/auth/contacts.other.readonly", + &services + )); + assert!(scope_matches_service( + "https://www.googleapis.com/auth/directory.readonly", + &services + )); + } + + #[test] + fn scope_matches_service_chat() { + let services: HashSet = ["chat"].iter().map(|s| s.to_string()).collect(); + assert!(scope_matches_service( + "https://www.googleapis.com/auth/chat.spaces", + &services + )); + assert!(scope_matches_service( + "https://www.googleapis.com/auth/chat.messages", + &services + )); + } + // ── services filter integration tests ──────────────────────────────── #[test]