oauth-setup
GitHub指导使用oauth_authorize工具配置Gmail等OAuth服务。强调必须使用Desktop-app客户端以避免重定向错误,正确设置scope并获取离线访问令牌。流程包括启动授权、等待回调及通过凭证存储调用API,严禁手动处理监听或读取密钥文件。
Trigger Scenarios
Install
npx skills add spinabot/brigade --skill oauth-setup -g -y
SKILL.md
Frontmatter
{
"name": "oauth-setup",
"metadata": {
"brigade": {
"emoji": "🔐"
}
},
"description": "Connect an OAuth 2.0 service (Gmail, Google APIs, etc.) using the built-in oauth_authorize tool — get a click-to-authorize link, auto-capture the code, and seal the tokens. Use whenever the operator asks to connect Gmail \/ a Google API \/ any OAuth account."
}
OAuth setup
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.
Version History
- db99206 Current 2026-07-05 10:59


