/auth-setup
Use when configuring OAuth providers (Google/GitHub/Apple/X/etc.), setting up post-login auth hooks, tuning JWT lifetimes, or generating service API keys
$ npx -y skills add butterbase-ai/butterbase-skills --skill auth-setup --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/auth-setup
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use when configuring OAuth providers (Google/GitHub/Apple/X/etc.), setting up post-login auth hooks, tuning JWT lifetimes, or generating service API keys
SKILL.md
auth-setup.SKILL.mdname: auth-setup
description: Use when configuring OAuth providers (Google/GitHub/Apple/X/etc.), setting up post-login auth hooks, tuning JWT lifetimes, or generating service API keys
Butterbase Auth Setup
Two umbrella tools cover end-user authentication:
- **`manage_oauth`** — provider configuration (Google, GitHub, Apple, X, custom)
- **`manage_auth_config`** — auth hooks, JWT lifetimes, service key generation
For broad app build-out, see also `butterbase-skills:build-app`. This skill is the deep dive.
---
1. The role model
Every request runs under one of three database roles:
| Auth header | Role | `current_user_id()` | RLS | |-------------|------|---------------------|-----| | _none_ | `butterbase_anon` | NULL | enforced; default deny | | End-user JWT (issued by `manage_oauth` or email login) | `butterbase_user` | user UUID | enforced | | Service key (`bb_sk_*`) | `butterbase_service` | NULL | bypassed |
Auth is what transforms a request into the right role. RLS is what filters the data. Both must be configured.
---
2. Configure an OAuth provider
manage_oauth({
app_id: "app_abc123",
action: "configure",
provider: "google",
client_id: "123456789.apps.googleusercontent.com",
client_secret: "GOCSPX-...",
redirect_uris: ["https://api.butterbase.ai/auth/app_abc123/oauth/google/callback"]
// scopes / authorization_url / token_url / userinfo_url / provider_metadata are auto-filled for built-in providers
})**Built-in providers (URLs and scopes pre-filled):** `google`, `github`, `discord`, `facebook`, `linkedin`, `microsoft`, `apple`, `x`.
**Custom providers:** pass `authorization_url`, `token_url`, `userinfo_url`, and `scopes` explicitly.
Redirect URI format
https://api.butterbase.ai/auth/{app_id}/oauth/{provider}/callbackRegister **this exact URI** in the provider's developer console. Mismatch is the most common reason OAuth flows fail.
Provider quirks
| Provider | Quirk | |----------|-------| | `apple` | Requires `provider_metadata: { teamId, keyId, privateKey }`. Apple only returns the user's name on **first** auth and uses POST callback (handled automatically). | | `x` | Does not return email. Butterbase synthesises `{username}@users.noreply.x.local` for the user record. | | `facebook` | Default scopes `email`, `public_profile`. | | `google` | Standard. | | `github` | Standard. |
List, update, delete
manage_oauth({ app_id, action: "get" }) // list all providers (secrets redacted)
manage_oauth({ app_id, action: "get", provider: "google" }) // single provider
manage_oauth({ app_id, action: "update", provider: "google", client_secret: "new-secret" })
manage_oauth({ app_id, action: "delete", provider: "google" }) // disables future logins; existing sessions valid until expiryFrontend flow
GET https://api.butterbase.ai/auth/{app_id}/oauth/{provider}?redirect_to=https://yourapp.com/auth/callbackUser signs in at the provider, gets bounced back to `redirect_to` with `access_token` and `refresh_token` as query params. The Butterbase SDK wraps this:
await client.auth.signInWithOAuth({ provider: "google" });
const { user, accessToken } = await client.auth.getSession();---
3. Tune JWT lifetimes
manage_auth_config({
app_id: "app_abc123",
action: "update_jwt",
accessTokenTtl: "15m", // formats: "15m", "1h", "2h", "1d"
refreshTokenTtlDays: 30 // integer days
})Defaults: 15-minute access tokens, 7-day refresh tokens.
| Use case | `accessTokenTtl` | `refreshTokenTtlDays` | |----------|------------------|------------------------| | High-security (banking, admin) | `5m`–`15m` | `1`–`7` | | Standard SaaS | `15m` (default) | `30` | | Low-friction consumer apps | `1h` | `90` |
**Important:** changes apply only to **new** tokens. Active tokens keep their original expiration — there is no global revoke. Treat TTL changes as forward-looking only.
---
4. Auth hooks (run code after every login)
A post-auth function is a deployed Butterbase function invoked **fire-and-forget** after every successful auth event (OAuth login, email login, email signup).
Wire it up
// 1. Deploy the function first (see butterbase-skills:function-dev)
deploy_function({
app_id: "app_abc123",
name: "after-auth",
code: postAuthHandlerCode,
trigger: { type: "http", config: { auth: "none" } }
})
// 2. Register it as the auth hook
manage_auth_config({
app_id: "app_abc123",
action: "configure_auth_hook",
post_auth_function: "after-auth"
})
// To remove the hook later: pass post_auth_function: nullThe function **must already exist** when you configure the hook.
Payload shape
The function receives a POST with this body:
{
"event": "oauth_login | login | signup",
"user": {
"id": "uuid",
"email": "...",
"provider": "google | github | email | ...",
"display_name": "...",
"avatar_url": "..."
},
"isNewUser": true,
"provider": "google"
}The function runs as `butterbase_service` (RLS bypassed, `ctx.user` is `null`). Use `body.user.id` to know who just logged in.
Common uses
// after-auth/index.ts
export async function handler(req, ctx) {
const { user, isNewUser, event } = await req.json();
if (isNewUser) {
// 1. Create profile row
await ctx.db.query(
"INSERT INTO profiles (user_id, display_name) VALUES ($1, $2) ON CONFLICT DO NOTHING",
[user.id, user.display_name]
);
// 2. Send welcome email (via env-stored API key)
ctx.waitUntil(sendWelcomeEmail(ctx.env.RESEND_API_KEY, user.email));
}
// 3. Audit log on every login
await ctx.db.query(
"INSERT INTO login_log (user_id, event, provider) VALUES ($1, $2, $3)",
[user.id, event, ctx.user ?? null]
);
return new Response("ok", { status: 200 });
}> Auth hooks are fire-and-forget. Don't return data the user needs — they won't see it. Use them for side effects
Read more
name: auth-setup description: Use when configuring OAuth providers (Google/GitHub/Apple/X/etc.), setting up post-login auth hooks, tuning JWT lifetimes, or generating service API keys
Butterbase Auth Setup
Two umbrella tools cover end-user authentication:
- **`manage_oauth`** — provider configuration (Google, GitHub, Apple, X, custom)
- **`manage_auth_config`** — auth hooks, JWT lifetimes, service key generation
For broad app build-out, see also `butterbase-skills:build-app`. This skill is the deep dive.
---
1. The role model
Every request runs under one of three database roles:
| Auth header | Role | `current_user_id()` | RLS | |-------------|------|---------------------|-----| | _none_ | `butterbase_anon` | NULL | enforced; default deny | | End-user JWT (issued by `manage_oauth` or email login) | `butterbase_user` | user UUID | enforced | | Service key (`bb_sk_*`) | `butterbase_service` | NULL | bypassed |
Auth is what transforms a request into the right role. RLS is what filters the data. Both must be configured.
---
2. Configure an OAuth provider
manage_oauth({
app_id: "app_abc123",
action: "configure",
provider: "google",
client_id: "123456789.apps.googleusercontent.com",
client_secret: "GOCSPX-...",
redirect_uris: ["https://api.butterbase.ai/auth/app_abc123/oauth/google/callback"]
// scopes / authorization_url / token_url / userinfo_url / provider_metadata are auto-filled for built-in providers
})**Built-in providers (URLs and scopes pre-filled):** `google`, `github`, `discord`, `facebook`, `linkedin`, `microsoft`, `apple`, `x`.
**Custom providers:** pass `authorization_url`, `token_url`, `userinfo_url`, and `scopes` explicitly.
Redirect URI format
https://api.butterbase.ai/auth/{app_id}/oauth/{provider}/callbackRegister **this exact URI** in the provider's developer console. Mismatch is the most common reason OAuth flows fail.
Provider quirks
| Provider | Quirk | |----------|-------| | `apple` | Requires `provider_metadata: { teamId, keyId, privateKey }`. Apple only returns the user's name on **first** auth and uses POST callback (handled automatically). | | `x` | Does not return email. Butterbase synthesises `{username}@users.noreply.x.local` for the user record. | | `facebook` | Default scopes `email`, `public_profile`. | | `google` | Standard. | | `github` | Standard. |
List, update, delete
manage_oauth({ app_id, action: "get" }) // list all providers (secrets redacted)
manage_oauth({ app_id, action: "get", provider: "google" }) // single provider
manage_oauth({ app_id, action: "update", provider: "google", client_secret: "new-secret" })
manage_oauth({ app_id, action: "delete", provider: "google" }) // disables future logins; existing sessions valid until expiryFrontend flow
GET https://api.butterbase.ai/auth/{app_id}/oauth/{provider}?redirect_to=https://yourapp.com/auth/callbackUser signs in at the provider, gets bounced back to `redirect_to` with `access_token` and `refresh_token` as query params. The Butterbase SDK wraps this:
await client.auth.signInWithOAuth({ provider: "google" });
const { user, accessToken } = await client.auth.getSession();---
3. Tune JWT lifetimes
manage_auth_config({
app_id: "app_abc123",
action: "update_jwt",
accessTokenTtl: "15m", // formats: "15m", "1h", "2h", "1d"
refreshTokenTtlDays: 30 // integer days
})Defaults: 15-minute access tokens, 7-day refresh tokens.
| Use case | `accessTokenTtl` | `refreshTokenTtlDays` | |----------|------------------|------------------------| | High-security (banking, admin) | `5m`–`15m` | `1`–`7` | | Standard SaaS | `15m` (default) | `30` | | Low-friction consumer apps | `1h` | `90` |
**Important:** changes apply only to **new** tokens. Active tokens keep their original expiration — there is no global revoke. Treat TTL changes as forward-looking only.
---
4. Auth hooks (run code after every login)
A post-auth function is a deployed Butterbase function invoked **fire-and-forget** after every successful auth event (OAuth login, email login, email signup).
Wire it up
// 1. Deploy the function first (see butterbase-skills:function-dev)
deploy_function({
app_id: "app_abc123",
name: "after-auth",
code: postAuthHandlerCode,
trigger: { type: "http", config: { auth: "none" } }
})
// 2. Register it as the auth hook
manage_auth_config({
app_id: "app_abc123",
action: "configure_auth_hook",
post_auth_function: "after-auth"
})
// To remove the hook later: pass post_auth_function: nullThe function **must already exist** when you configure the hook.
Payload shape
The function receives a POST with this body:
{
"event": "oauth_login | login | signup",
"user": {
"id": "uuid",
"email": "...",
"provider": "google | github | email | ...",
"display_name": "...",
"avatar_url": "..."
},
"isNewUser": true,
"provider": "google"
}The function runs as `butterbase_service` (RLS bypassed, `ctx.user` is `null`). Use `body.user.id` to know who just logged in.
Common uses
// after-auth/index.ts
export async function handler(req, ctx) {
const { user, isNewUser, event } = await req.json();
if (isNewUser) {
// 1. Create profile row
await ctx.db.query(
"INSERT INTO profiles (user_id, display_name) VALUES ($1, $2) ON CONFLICT DO NOTHING",
[user.id, user.display_name]
);
// 2. Send welcome email (via env-stored API key)
ctx.waitUntil(sendWelcomeEmail(ctx.env.RESEND_API_KEY, user.email));
}
// 3. Audit log on every login
await ctx.db.query(
"INSERT INTO login_log (user_id, event, provider) VALUES ($1, $2, $3)",
[user.id, event, ctx.user ?? null]
);
return new Response("ok", { status: 200 });
}> Auth hooks are fire-and-forget. Don't return data the user needs — they won't see it. Use them for side effects
Claude Code plugin for Butterbase — the AI-Native Backend-as-a-Service. This plugin gives Claude deep knowledge of Butterbase's 42+ MCP tools, guides you through common workflows, and auto-configures the MCP server connection.
Repo: butterbase-ai/butterbase-skills
Other skills on butterbase-skills.
- /agents
Use when designing, deploying, or debugging a Butterbase Agent (declarative LLM/tool graph), registering an MCP server for tool use, or wiring access controls and rate limits. Agents are first-class app resources defined by a `graph_spec` and invoked over
Open skill - /ai
Use when calling the app's AI gateway from agent tools — chat completions, embeddings, listing models, configuring defaults or BYOK, reading token/cost usage
Open skill - /build-app
Use when building a new Butterbase app from scratch, creating a full-stack application, or when the user asks to set up a complete backend with database, auth, and deployment
Open skill - /contributing
Use when contributing to the Butterbase codebase, adding new MCP tools, creating API routes, writing migrations, or understanding the monorepo architecture
Open skill - /debug-rls
Use when users report access denied errors, see wrong data, RLS policies are not working, or when troubleshooting Row-Level Security issues in Butterbase
Open skill - /deploy-frontend
Use when deploying a frontend (React, Next.js, or static HTML) to a live URL on Butterbase, or when troubleshooting deployment issues like MIME type errors or blank pages
Open skill

