OAuth setup
SkillCommunicationThe oauth-setup skill connects an OAuth 2.0 service such as Gmail or another Google API to your agent using the built-in oauth_authorize tool. It starts an authorization flow, gives the operator a click-to-authorize link, captures the returned code, and seals the tokens into the credential store.
Then ask your AI: use the OAuth setup skill
Details
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; Ahel provides instructions and does not run this skill.
No other account needed.
Create a Desktop-app OAuth client in the provider console, enable the API you need, and keep the consent screen in Testing with yourself as a test user.
What your AI can do with it
- Start an OAuth flow with oauth_authorize action start
- Return a click-to-authorize link for the operator
- Capture the redirect code and seal tokens in the credential store
- List connected accounts with oauth_authorize action status
- Fetch a live access token with automatic refresh
- Call APIs such as Gmail send with the connected account
Getting started
- Create a Desktop-app OAuth client in the provider console, enable the API you need, and keep the consent screen in Testing with yourself as a test user.
- Give the agent the client id and client secret from that Desktop-app client.
- Start the flow with oauth_authorize action start, passing the provider, endpoints, client id, secret, and the real scopes.
- Send the returned authUrl to the operator to click, then call oauth_authorize action await with the flowId.
- If await returns status pending, tell the operator and call await again.
What this skill tells your AI
The instructions your AI receives, as published by spinabot/brigade in skills/oauth-setup/SKILL.md and read by Ahel’s review.
Use the oauth_authorize tool — NEVER hand-roll an http listener in bash. It opens a one-shot loopback listener on a free port (no EADDRINUSE, no port juggling), captures the code, and seals the tokens into the credential store.
The two mistakes to avoid (they cost a whole session once)
- Use a Desktop-app OAuth client, not a Web client. In Google Cloud Console → Credentials → Create OAuth client ID → Application type: Desktop app. Desktop clients accept ANY
http://127.0.0.1:<port>loopback redirect with no redirect-URI registration. A Web client requires the exact redirect URI pre-registered, which is what causesredirect_uri_mismatch. - Use the real scope. Gmail send is
https://www.googleapis.com/auth/gmail.send(NOThttps://gmail.com/..., which doesn't exist). Addopenid https://www.googleapis.com/auth/userinfo.emailif you want the account address auto-filled.
Flow
- Ask the operator to create a Desktop-app OAuth client (Gmail API enabled, consent screen in Testing with themselves as a test user) and paste the client id + secret.
- Start the flow:
oauth_authorize({ action: "start", provider: "google-gmail", authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth", tokenEndpoint: "https://oauth2.googleapis.com/token", userInfoEndpoint: "https://www.googleapis.com/oauth2/v3/userinfo", clientId: "<id>", clientSecret: "<secret>", scopes: ["https://www.googleapis.com/auth/gmail.send", "openid", "https://www.googleapis.com/auth/userinfo.email"], extraAuthParams: { access_type: "offline", prompt: "consent" } })access_type=offline+prompt=consentare required for a refresh token (otherwise re-auth is needed when the access token expires). - Send the operator the returned
authUrlto click. - Await the redirect — it exchanges the code and seals the tokens:
If it returns statusoauth_authorize({ action: "await", flowId: "<flowId from start>" })pending, the operator hasn't clicked yet — tell them, then callawaitagain.
Using a connected account (sending mail)
The tokens are sealed in the credential store — you can't read them from a file or the DB, and you shouldn't try. To use a connected account:
- Check what's connected:
oauth_authorize({ action: "status" })→ lists each account (provider, email, scopes, expiry). No secrets. - Get a usable token:
oauth_authorize({ action: "token", provider: "google-gmail" })→ returnsaccessToken. It auto-refreshes from the sealed refresh token when the old one expired, so you always get a live token. (Passprovideronly if more than one account is connected.) - Call the API with it. Gmail send:
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send, headerAuthorization: Bearer <accessToken>, body{ "raw": "<base64url RFC-822 message>" }.
Never cat the credential store, grep for the token, or hand-roll a refresh — action:"token" is the only retrieval path, and it keeps the refresh token + client secret sealed.
Signals
- GitHub stars
- 11k
- Forks
- 70
- Last commit
- Oct 2026
Questions
- Why does the flow fail with redirect_uri_mismatch?
- A Web OAuth client requires the exact redirect URI to be pre-registered. Use a Desktop-app client instead: it accepts any http://127.0.0.1:<port> loopback redirect with no redirect-URI registration.
- Which scope should I use for Gmail send?
- Use https://www.googleapis.com/auth/gmail.send. The scope https://gmail.com/... does not exist. Add openid and https://www.googleapis.com/auth/userinfo.email if you want the account address auto-filled.
Advanced
- Item type
- skill
- Key
oauth-setup- Source
- github.com/spinabot/brigade
Related picks
Skill · googleworkspace
The pick for Gmailopenakita/skills@gmail-automation
Skill · openakita
The pick for Gmailslack-gif-creator
Skill · anthropics
More in Communicationerror-handling
Skill · affaan-m
More in Communicationemails
Skill · coreyhaines31
More in Communicationwait-what
Skill · mattpocock
More in Communication