tapauth
v1.4.0Use when you need an OAuth token for a user's account — Google Calendar, Gmail, GitHub, Slack, Linear, Notion, Vercel, Sentry, Asana, Discord, or Google Docs/Sheets. No API key or credentials needed. No setup. Just run the bundled script and it handles everything: grant creation, user approval, toke...
Installation
TapAuth — Delegated Access for AI Agents
TapAuth lets your agent get OAuth tokens from users without handling credentials directly. The user approves in their browser. You get a scoped token. That's it.
How It Works
This skill includes a CLI script at scripts/tapauth.sh. Use it inline with curl:
curl -H "Authorization: Bearer $(scripts/tapauth.sh google calendar.readonly)"
"https://www.googleapis.com/calendar/v3/calendars/primary/events"
That's a complete, working example. The script handles everything:
- First run: Creates a grant, prints an approval URL to stderr, polls until the user approves, then outputs the token to stdout.
- Subsequent runs: Returns the cached token instantly. Automatically refreshes expired tokens.
The $(...) command substitution captures stdout (the token) while the approval URL goes to stderr — so curl gets a clean Bearer token.
Gotchas
- No API key or credentials needed. TapAuth is zero-config. Do not look for API keys, client secrets, or environment variables. Just run the script.
- Always use the bundled script. The script is at
scripts/tapauth.shinside this skill. Do NOT download it from the website — you already have it. - Approval URL goes to stderr, token goes to stdout. The
$(scripts/tapauth.sh ...)pattern works because the token is on stdout and the approval URL is on stderr. Don't redirect stderr to stdout or the token will be corrupted. - Surface the approval URL to the user. The script prints it to stderr. The user must click the link, sign in with their account, and approve. Nothing happens until they do.
- Scopes are provider-specific. Check
references/<provider>.mdfor valid scopes. Google uses URL-style scopes (calendar.readonly), GitHub uses words (repo), others vary. - Tokens are cached automatically. After the first approval, subsequent runs return the cached token instantly. Don't create new grants when you already have a cached token.
- Use focused Google providers when possible.
google_sheetsorgoogle_docsgive simpler consent screens thangooglewith full scopes.
Usage Pattern
The pattern is always the same:
curl -H "Authorization: Bearer $(scripts/tapauth.sh <provider> <scopes>)"
<api-url>
For requests that need a body:
curl -X POST
-H "Authorization: Bearer $(scripts/tapauth.sh <provider> <scopes>)"
-H "Content-Type: application/json"
-d '{"key": "value"}'
<api-url>
If you need the token for multiple requests, capture it once:
TOKEN=$(scripts/tapauth.sh github repo)
# Use it in multiple calls
curl -H "Authorization: Bearer $TOKEN"
"https://api.github.com/user/repos?per_page=5"
curl -X POST -H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/json"
-d '{"title": "Bug report", "body": "Details here"}'
"https://api.github.com/repos/owner/repo/issues"
First-Run Flow
On first run, the script will:
- Create a grant via the TapAuth API
- Print an approval URL to stderr — show this to the user
- Poll every 2 seconds (up to 10 minutes) waiting for approval
- Once approved, output the token to stdout and cache it locally
Example stderr output:
Creating grant for google (calendar.readonly)...
Approve access: https://tapauth.ai/approve/abc123
Waiting for approval... (2s) https://tapauth.ai/approve/abc123
Waiting for approval... (4s) https://tapauth.ai/approve/abc123
Important: Surface the approval URL to the user clearly. They need to click it, sign in, and approve.
Real-World Examples
List Google Calendar events
curl -s -H "Authorization: Bearer $(scripts/tapauth.sh google calendar.readonly)"
"https://www.googleapis.com/calendar/v3/calendars/primary/events?maxResults=10&orderBy=startTime&singleEvents=true&timeMin=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
Read a GitHub repo's issues
curl -s -H "Authorization: Bearer $(scripts/tapauth.sh github repo)"
"https://api.github.com/repos/owner/repo/issues?state=open&per_page=10"
Create a GitHub issue
curl -s -X POST
-H "Authorization: Bearer $(scripts/tapauth.sh github repo)"
-H "Content-Type: application/json"
-d '{"title": "Fix login bug", "body": "Steps to reproduce..."}'
"https://api.github.com/repos/owner/repo/issues"
Send an email via Gmail
# Base64-encode the email
EMAIL=$(printf "To: [email protected]: HellornrnMessage body" | base64)
curl -s -X POST
-H "Authorization: Bearer $(scripts/tapauth.sh google https://www.googleapis.com/auth/gmail.send)"
-H "Content-Type: application/json"
-d "{"raw": "$EMAIL"}"
"https://www.googleapis.com/gmail/v1/users/me/messages/send"
Query Linear issues
curl -s -X POST
-H "Authorization: Bearer $(scripts/tapauth.sh linear read)"
-H "Content-Type: application/json"
-d '{"query": "{ issues(first: 10) { nodes { title state { name } } } }"}'
"https://api.linear.app/graphql"
Search Notion
curl -s -X POST
-H "Authorization: Bearer $(scripts/tapauth.sh notion)"
-H "Content-Type: application/json"
-H "Notion-Version: 2022-06-28"
-d '{"query": "meeting notes"}'
"https://api.notion.com/v1/search"
List Google Drive files
curl -s -H "Authorization: Bearer $(scripts/tapauth.sh google drive.readonly)"
"https://www.googleapis.com/drive/v3/files?pageSize=10&fields=files(id,name,mimeType)"
List Vercel deployments
curl -s -H "Authorization: Bearer $(scripts/tapauth.sh vercel)"
"https://api.vercel.com/v6/deployments?limit=5"
Configuration
Environment variables:
- TAPAUTH_BASE_URL — Override the base URL (default: https://tapauth.ai)
- TAPAUTH_HOME — Override the cache directory (takes highest priority)
- CLAUDE_PLUGIN_DATA — Stable per-plugin directory provided by Claude Code (used automatically if set)
Cache directory priority: TAPAUTH_HOME > CLAUDE_PLUGIN_DATA > ./.tapauth
Caching: Tokens are stored in the cache directory (mode 700, files mode 600). Each provider+scope combination gets its own cache file with the token, expiry, grant ID, and grant secret for automatic refresh.
Supported Providers
See references/ for provider-specific scopes, examples, and API details:
| Provider | ID | Scopes Reference |
|---|---|---|
| GitHub | github |
references/github.md |
| Google (multi-service) | google |
references/google.md |
| Gmail | google with gmail scopes |
references/gmail.md |
| Google Sheets | google_sheets |
references/google_sheets.md |
| Google Docs | google_docs |
references/google_docs.md |
| Linear | linear |
references/linear.md |
| Vercel | vercel |
references/vercel.md |
| Notion | notion |
references/notion.md |
| Slack | slack |
references/slack.md |
| Sentry | sentry |
references/sentry.md |
| Asana | asana |
references/asana.md |
| Discord | discord |
references/discord.md |
Tip: Use
google_sheetsorgoogle_docswhen you only need one Google service. Use
To list all providers and valid scopes programmatically:
curl -s https://tapauth.ai/api/providers
Provider Notes
- GitHub: The
reposcope grants read/write to repositories. Useread:userfor profile info only. - Google: All Google providers support automatic token refresh. Use focused providers (
google_sheets,google_docs) for simpler consent screens. - Linear/Notion/Slack/Vercel: Scopes are fixed at integration level, not per-request. The scope argument is still required but may be ignored.
- Discord: User OAuth tokens, not bot tokens. Tokens expire after ~7 days with automatic refresh.
Token Lifetimes & Revocation
TapAuth uses zero-knowledge encryption — tokens are encrypted with your grant_secret, which TapAuth never stores. This means:
- TapAuth cannot revoke tokens at the provider level. We literally cannot decrypt them.
- When a grant expires, the encrypted ciphertext is deleted without ever being read.
- Short-lived tokens (Google ~1hr, Linear ~1hr, Sentry ~8hr) expire naturally and auto-refresh.
- Long-lived tokens (GitHub, Slack, Vercel, Notion) must be revoked in provider settings if needed.
Common Patterns
Ask the user to approve, then proceed
1. Run scripts/tapauth.sh <provider> <scopes>
2. It prints an approval URL to stderr — show this to the user
3. It polls automatically until approved (up to 10 minutes)
4. Use the returned token in your API calls
Handle expiry gracefully
If the cached token has expired, the script automatically refreshes it. If refresh fails, delete .tapauth/ and re-run to create a fresh grant.
Scope selection
Request the minimum scopes you need. Users see exactly what you're asking for and can approve with reduced permissions. Less scope = more trust = higher approval rate.
The Raw API (Advanced)
If you can't use the CLI script, the API flow is:
- Create grant:
POST https://tapauth.ai/api/v1/grantswithproviderandscopes - User approves at the returned
approve_url - Get token:
GET https://tapauth.ai/api/v1/token/{grant_id}withAuthorization: Bearer {grant_secret}
| Status | Meaning |
|---|---|
| 200 | Token ready |
| 202 | Pending — poll again in 2-5 seconds |
| 401 | Invalid grant_secret |
| 404 | Grant not found |
| 410 | Expired, revoked, or denied |
See the API docs for full details on request/response formats.
Common Issues
| Error | Cause | Solution |
|---|---|---|
tapauth: failed to create grant |
Invalid provider or scopes | Check references/ for valid provider IDs and scope formats |
| Token expired / 401 on API call | Cached token expired, refresh failed | Delete .tapauth/ and re-run to create a fresh grant |
| Approval URL not visible | stderr suppressed by agent runtime | Capture stderr separately or check .tapauth/ for pending grant files |
tapauth: timed out after 600s |
User didn't approve within 10 minutes | Re-run to create a new grant with a fresh approval URL |
OpenClaw Secrets Provider
TapAuth supports the OpenClaw exec secrets provider protocol via the tapauth-secrets script. This lets OpenClaw agents resolve OAuth tokens as secrets at startup.
Configure in your OpenClaw agent config:
{
"secrets": {
"tapauth": {
"source": "exec",
"command": ["/path/to/tapauth-secrets"],
"passEnv": ["HOME", "TAPAUTH_HOME", "TAPAUTH_BASE_URL"]
}
}
}
Reference tokens as tapauth.provider/scopes (e.g. tapauth.google/calendar.readonly).
Note: Grants must be pre-approved — tapauth-secrets uses a 10-second timeout and cannot prompt for interactive approval.