Introduction
# Gmail OAuth Setup
Headless-friendly OAuth flow for Gmail API access using `gog` CLI.
## Prerequisites
- `gog` CLI installed (`brew install steipete/tap/gogcli`) - Google Cloud project with OAuth credentials (Desktop app type) - Gmail API enabled in the project
## Quick Setup
### 1. Create Google Cloud Project & Credentials
1. Go to https://console.cloud.google.com 2. Create a new project (or select existing) 3. **Enable Gmail API**: APIs & Services → Library → search "Gmail API" → Enable 4. **Configure OAuth consent screen**: APIs & Services → OAuth consent screen - Choose "External" user type - Fill in app name, user support email - Add scopes: `gmail.modify` (or others as needed) - **Important**: Click "PUBLISH APP" for permanent tokens (see Troubleshooting) 5. **Create credentials**: APIs & Services → Credentials → Create Credentials → OAuth client ID - Application type: **Desktop app** - Download the JSON file
### 2. Configure gog
```bash gog auth credentials /path/to/client_secret.json gog auth keyring file # Use file-based keyring for headless export GOG_KEYRING_PASSWORD="your-password" # Add to .bashrc ```
### 3. Run Auth Flow
Run `scripts/gmail-auth.sh` interactively, or:
```bash # Generate URL scripts/gmail-auth.sh --url
# User opens URL, approves, copies code from localhost redirect # Exchange code (do this quickly - codes expire in minutes!) scripts/gmail-auth.sh --exchange CODE EMAIL ```
### 4. Verify
```bash gog gmail search 'is:unread' --max 5 --account [email protected] ```
## Troubleshooting
### "Access blocked: [app] has not completed the Google verification process"
**Cause**: App is in "Testing" mode and the Gmail account isn't a test user.
**Solutions** (choose one): 1. **Publish the app** (recommended): - Google Cloud Console → APIs & Services → OAuth consent screen - Click **"PUBLISH APP"** → Confirm - No Google review needed for personal use - Tokens become permanent
2. **Add test user**: - OAuth consent screen → Test users → + ADD USERS - Add the Gmail address you're authorizing - Tokens still expire in 7 days
### "Google hasn't verified this app" warning screen
**This is normal for personal apps.** Click: 1. **Advanced** (bottom left) 2. **Go to [app name] (unsafe)**
Safe to proceed since you own the app.
### Token expires in 7 days
**Cause**: App is in "Testing" mode.
**Fix**: Publish the app (see above). Published apps get permanent refresh tokens.
### "invalid_request" or "invalid_grant" errors
**Causes**: - Authorization code expired (they only last a few minutes) - Code was already used - Redirect URI mismatch
**Fix**: Generate a fresh auth URL and complete the flow quickly. Paste the code immediately after getting it.
### "redirect_uri_mismatch" error
**Cause**: The redirect URI in the token exchange doesn't match what was used in the auth URL.
**Fix**: This script uses `http://localhost`. Make sure both the auth URL and exchange use the same redirect URI.
### Page hangs after approving permissions (mobile)
**Cause**: Browser trying to connect to localhost which doesn't exist on phone.
**Fix**: - Use a desktop browser instead - Or tap the address bar while it's "hanging" - the URL contains the code - The URL will look like: `http://localhost/?code=4/0ABC...`
### Multiple permission checkboxes causing hangs
**Cause**: Too many OAuth scopes requested.
**Fix**: Use minimal scopes. `gmail.modify` alone is usually sufficient and shows just one permission.
### Can't find project in Google Cloud Console
**Cause**: Signed into wrong Google account.
**Fix**: Check which account owns the project: - Click profile icon (top right) - Switch accounts - Check project dropdown for each account
### "invalid_request" with oob redirect (new projects)
**Cause**: Google deprecated `urn:ietf:wg:oauth:2.0:oob` for OAuth clients created after 2022.
**Fix**: Use `http://localhost` redirect instead (this script's default). After approval, browser redirects to localhost with code in URL.
## Scopes Reference
| Scope | Access | |-------|--------| | `gmail.modify` | Read, send, delete, manage labels (recommended) | | `gmail.readonly` | Read only | | `gmail.send` | Send only | | `gmail.compose` | Create drafts, send |
## Files
- `scripts/gmail-auth.sh` — Interactive auth helper
## Tips
- **Publish your app** — Avoids test user limits and 7-day token expiry - **Exchange codes quickly** — They expire in minutes - **Use desktop browser** — Mobile browsers can be finicky with localhost redirects - **One scope is enough** — `gmail.modify` covers most use cases