-
Notifications
You must be signed in to change notification settings - Fork 796
Description
Description
The long-running helper commands gmail +watch and events +subscribe obtain OAuth2 access tokens once at startup and pass them as static &str values into their pull loops. Since Google access tokens expire after 3600 seconds (1 hour), the Pub/Sub pull request eventually receives a 401 response, which the loop treats as a fatal error and exits.
Steps to reproduce
gws gmail +watch \
--subscription projects/my-project/subscriptions/my-sub \
--label-ids INBOX \
--poll-interval 5Wait ~60 minutes. The process exits with status 1.
Expected behavior
The +watch command should run indefinitely (up to the 7-day Gmail watch expiration), automatically refreshing the access token before or when it expires.
Actual behavior
The process exits with a non-zero status after ~1 hour on every run. The lifetime is consistent across restarts, matching the Google OAuth2 access token TTL of 3600 seconds.
Root cause
In src/helpers/gmail/watch.rs, tokens are obtained once before the loop:
// watch.rs lines 17-22
let gmail_token = auth::get_token(&[GMAIL_SCOPE]).await …;
let pubsub_token = auth::get_token(&[PUBSUB_SCOPE]).await …;auth::get_token() (in src/auth.rs line 91) builds a yup_oauth2 authenticator, calls .token() once to get the access token as a String, then drops the authenticator, discarding its refresh capability.
The tokens are passed into watch_pull_loop() as &str:
// watch.rs lines 247-254
async fn watch_pull_loop(
client: &reqwest::Client,
pubsub_token: &str,
gmail_token: &str,
…When the token expires after 3600s, the next Pub/Sub pull returns a 401, which hits the fatal error path at line 282-289:
if !resp.status().is_success() {
let body = resp.text().await.unwrap_or_default();
return Err(GwsError::Api { … });
}The same pattern exists in src/helpers/events/subscribe.rs (pull_loop takes token: &str).
The HTTP client (src/client.rs) is a plain reqwest::Client with no auth middleware.
Suggested fix
Retain the yup_oauth2 authenticator and call .token() before each API request (or on a timer), rather than extracting the access token string once.
Alternatively, get_token() could be called periodically inside the loop (e.g., every 45 minutes or upon receiving a 401).
Affected commands
gws gmail +watch(src/helpers/gmail/watch.rs)gws events +subscribe(src/helpers/events/subscribe.rs)- Potentially any future long-running helper that calls
auth::get_token()once
Version
gws 0.8.0
Environment
Linux