---
name: gmail-oauth
description: Set up Gmail API access via gog CLI with manual OAuth flow. Use when setting up Gmail integration, renewing expired OAuth tokens, or troubleshooting Gmail authentication on headless servers.
---

# 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 you@gmail.com
```

## 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
