$npx -y skills add butterbase-ai/butterbase-skills --skill auth-setupUse when configuring OAuth providers (Google/GitHub/Apple/X/etc.), setting up post-login auth hooks, tuning JWT lifetimes, or generating service API keys
| 1 | # Butterbase Auth Setup |
| 2 | |
| 3 | Two umbrella tools cover end-user authentication: |
| 4 | |
| 5 | - **`manage_oauth`** — provider configuration (Google, GitHub, Apple, X, custom) |
| 6 | - **`manage_auth_config`** — auth hooks, JWT lifetimes, service key generation |
| 7 | |
| 8 | For broad app build-out, see also `butterbase-skills:build-app`. This skill is the deep dive. |
| 9 | |
| 10 | --- |
| 11 | |
| 12 | ## 1. The role model |
| 13 | |
| 14 | Every request runs under one of three database roles: |
| 15 | |
| 16 | | Auth header | Role | `current_user_id()` | RLS | |
| 17 | |-------------|------|---------------------|-----| |
| 18 | | _none_ | `butterbase_anon` | NULL | enforced; default deny | |
| 19 | | End-user JWT (issued by `manage_oauth` or email login) | `butterbase_user` | user UUID | enforced | |
| 20 | | Service key (`bb_sk_*`) | `butterbase_service` | NULL | bypassed | |
| 21 | |
| 22 | Auth is what transforms a request into the right role. RLS is what filters the data. Both must be configured. |
| 23 | |
| 24 | --- |
| 25 | |
| 26 | ## 2. Configure an OAuth provider |
| 27 | |
| 28 | ```js |
| 29 | manage_oauth({ |
| 30 | app_id: "app_abc123", |
| 31 | action: "configure", |
| 32 | provider: "google", |
| 33 | client_id: "123456789.apps.googleusercontent.com", |
| 34 | client_secret: "GOCSPX-...", |
| 35 | redirect_uris: ["https://api.butterbase.ai/auth/app_abc123/oauth/google/callback"] |
| 36 | // scopes / authorization_url / token_url / userinfo_url / provider_metadata are auto-filled for built-in providers |
| 37 | }) |
| 38 | ``` |
| 39 | |
| 40 | **Built-in providers (URLs and scopes pre-filled):** `google`, `github`, `discord`, `facebook`, `linkedin`, `microsoft`, `apple`, `x`. |
| 41 | |
| 42 | **Custom providers:** pass `authorization_url`, `token_url`, `userinfo_url`, and `scopes` explicitly. |
| 43 | |
| 44 | ### Redirect URI format |
| 45 | |
| 46 | ``` |
| 47 | https://api.butterbase.ai/auth/{app_id}/oauth/{provider}/callback |
| 48 | ``` |
| 49 | |
| 50 | Register **this exact URI** in the provider's developer console. Mismatch is the most common reason OAuth flows fail. |
| 51 | |
| 52 | ### Provider quirks |
| 53 | |
| 54 | | Provider | Quirk | |
| 55 | |----------|-------| |
| 56 | | `apple` | Requires `provider_metadata: { teamId, keyId, privateKey }`. Apple only returns the user's name on **first** auth and uses POST callback (handled automatically). | |
| 57 | | `x` | Does not return email. Butterbase synthesises `{username}@users.noreply.x.local` for the user record. | |
| 58 | | `facebook` | Default scopes `email`, `public_profile`. | |
| 59 | | `google` | Standard. | |
| 60 | | `github` | Standard. | |
| 61 | |
| 62 | ### List, update, delete |
| 63 | |
| 64 | ```js |
| 65 | manage_oauth({ app_id, action: "get" }) // list all providers (secrets redacted) |
| 66 | manage_oauth({ app_id, action: "get", provider: "google" }) // single provider |
| 67 | manage_oauth({ app_id, action: "update", provider: "google", client_secret: "new-secret" }) |
| 68 | manage_oauth({ app_id, action: "delete", provider: "google" }) // disables future logins; existing sessions valid until expiry |
| 69 | ``` |
| 70 | |
| 71 | ### Frontend flow |
| 72 | |
| 73 | ``` |
| 74 | GET https://api.butterbase.ai/auth/{app_id}/oauth/{provider}?redirect_to=https://yourapp.com/auth/callback |
| 75 | ``` |
| 76 | |
| 77 | User 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: |
| 78 | |
| 79 | ```ts |
| 80 | await client.auth.signInWithOAuth({ provider: "google" }); |
| 81 | const { user, accessToken } = await client.auth.getSession(); |
| 82 | ``` |
| 83 | |
| 84 | --- |
| 85 | |
| 86 | ## 3. Tune JWT lifetimes |
| 87 | |
| 88 | ```js |
| 89 | manage_auth_config({ |
| 90 | app_id: "app_abc123", |
| 91 | action: "update_jwt", |
| 92 | accessTokenTtl: "15m", // formats: "15m", "1h", "2h", "1d" |
| 93 | refreshTokenTtlDays: 30 // integer days |
| 94 | }) |
| 95 | ``` |
| 96 | |
| 97 | Defaults: 15-minute access tokens, 7-day refresh tokens. |
| 98 | |
| 99 | | Use case | `accessTokenTtl` | `refreshTokenTtlDays` | |
| 100 | |----------|------------------|------------------------| |
| 101 | | High-security (banking, admin) | `5m`–`15m` | `1`–`7` | |
| 102 | | Standard SaaS | `15m` (default) | `30` | |
| 103 | | Low-friction consumer apps | `1h` | `90` | |
| 104 | |
| 105 | **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. |
| 106 | |
| 107 | --- |
| 108 | |
| 109 | ## 4. Auth hooks (run code after every login) |
| 110 | |
| 111 | A post-auth function is a deployed Butterbase function invoked **fire-and-forget** after every successful auth event (OAuth login, email login, email signup). |
| 112 | |
| 113 | ### Wire it up |
| 114 | |
| 115 | ```js |
| 116 | // 1. Deploy the function first (see butterbase-skills:function-dev) |
| 117 | deploy_function({ |
| 118 | app_id: "app_abc123", |
| 119 | name: "after-auth", |
| 120 | code: postAuthHandlerCode, |
| 121 | trigger: { type: "http", config: { auth: "none" } } |
| 122 | }) |
| 123 | |
| 124 | // 2. Register it as the auth hook |
| 125 | manage_auth_config({ |
| 126 | app_id: "app_abc123", |
| 127 | action: "configure_auth_hook", |
| 128 | post_auth_function: "after-auth" |
| 129 | }) |
| 130 | |
| 131 | // To remove the hook later: pass post_auth_function: null |
| 132 | ``` |
| 133 | |
| 134 | The function **must already exist** when you configure the hook. |
| 135 | |
| 136 | ### Payload shape |
| 137 | |
| 138 | The function receives a POST with this body: |
| 139 | |
| 140 | ```json |
| 141 | { |
| 142 | "event": "oauth_login | login | signup", |
| 143 | "user": { |
| 144 | "id": "uuid", |
| 145 | "email": "...", |
| 146 | "provider": "google | github | email | ...", |
| 147 | "display_name": "...", |
| 148 | "avatar_url": "..." |
| 149 | }, |
| 150 | "is |