Skip to content

OAuth Providers

AnvilBase supports social login through eight OAuth providers, configured per project: github, google, apple, discord, microsoft, facebook, twitter, and linkedin.

How it works

  1. You register an OAuth app with the provider and get a client id + client secret.
  2. You add the provider to your AnvilBase project (client id/secret + scopes). When the provider is enabled with both credentials, AnvilBase builds the per-project Better Auth socialProviders config from it (client secrets are stored encrypted at rest and decrypted only for use).
  3. Your app starts the flow at the GoTrue-compatible GET /v1/auth/<project_id>/auth/v1/authorize?provider=<p>&redirect_to=<url> — exactly what supabase-js signInWithOAuth hits. AnvilBase 302s the browser to the provider’s consent screen.
  4. After consent, the provider redirects back through AnvilBase’s callback, which mints a project session (JWT pair) and 302s the browser to your redirect_to with the tokens in the URL fragment: …#access_token=…&refresh_token=…&token_type=bearer&expires_in=3600. Your landing page adopts the session (the SDK’s getSessionFromUrl does this).

Security — open-redirect protection. redirect_to is validated against the full-URL allow-list AUTH_ALLOWED_REDIRECT_URLS (comma-separated; scheme + host + path matched) at both /authorize and /callback. A redirect_to that isn’t on the list is rejected with 400. This is mandatory — an unvalidated OAuth redirect is a token-exfiltration hole. Secure-by-default: when the list is empty, no redirect target is allowed.

Register the OAuth app (provider side)

Set the callback / redirect URL at the provider to AnvilBase’s native Better Auth callback (this is the URL the provider calls, distinct from your app’s redirect_to):

https://<your-host>/v1/auth/<project_id>/api/project-auth/callback/<provider>

(For local testing, swap the host for http://localhost:39001.)

Provider consoles:

  • GitHub — Settings → Developer settings → OAuth Apps
  • Google — Google Cloud Console → APIs & Services → Credentials
  • Apple — Apple Developer → Certificates, Identifiers & Profiles → Services IDs
  • Discord — Discord Developer Portal → Applications → OAuth2
  • Microsoft — Azure Portal → App registrations
  • Facebook — Meta for Developers → My Apps → Facebook Login
  • Twitter — X Developer Portal → Projects & Apps → User authentication settings
  • LinkedIn — LinkedIn Developers → Apps → Auth

Add the provider to your project

Terminal window
curl -X PUT http://localhost:39001/api/v1/projects/<id>/auth/providers \
-H "Authorization: Bearer $ANVILBASE_TOKEN" -H "Content-Type: application/json" \
-d '{
"provider": "github",
"enabled": true,
"client_id": "Iv1.abc123",
"client_secret": "ghs_…",
"scopes": ["read:user", "user:email"]
}'
  • Both client_id and client_secret are required.
  • scopes is optional — when empty, the provider’s recommended default scopes are used.
  • Returns 201 when newly added, 200 when replacing an existing provider.
Terminal window
curl http://localhost:39001/api/v1/auth/templates -H "Authorization: Bearer $ANVILBASE_TOKEN"

Lists each provider’s template and recommended scopes.

Read current configuration

Provider secrets come back masked:

Terminal window
curl http://localhost:39001/api/v1/projects/<id>/auth -H "Authorization: Bearer $ANVILBASE_TOKEN"
{
"email_enabled": true,
"magic_link_enabled": false,
"providers": [
{
"provider": "github",
"enabled": true,
"client_id": "Iv1.abc123",
"client_secret_masked": "ghs_…cret",
"scopes": ["read:user", "user:email"]
}
]
}

Remove a provider

Terminal window
curl -X DELETE http://localhost:39001/api/v1/projects/<id>/auth/providers/github \
-H "Authorization: Bearer $ANVILBASE_TOKEN"

Start the login flow from your app

With the AnvilBase SDK (all 7 clients expose signInWithOAuth):

// signInWithOAuth builds the /authorize URL and (in a browser) navigates to it.
await db.auth.signInWithOAuth({
provider: "github",
options: { redirectTo: "https://app.example.com/auth/callback" },
})
// On your callback page, adopt the session from the URL fragment:
db.auth.getSessionFromUrl(window.location.href)

Or hit the endpoint directly:

window.location.href =
`${HOST}/v1/auth/${PROJECT_ID}/auth/v1/authorize` +
`?provider=github&redirect_to=${encodeURIComponent(REDIRECT)}`

After the provider redirects back through AnvilBase’s callback, the browser lands on your redirect_to with the session tokens in the URL fragment. The oauth.callback audit event fires on a successful sign-in.

Native id-token sign-in

Native mobile apps (Sign-in-with-Apple / Google on iOS/Android) obtain a provider id-token directly from the OS SDK and exchange it server-side — there is no browser redirect. AnvilBase exposes the GoTrue-compatible grant for this:

POST /v1/auth/<project_id>/auth/v1/token?grant_type=id_token
Content-Type: application/json
{ "provider": "apple", "token": "<provider-id-token>", "nonce": "<optional>", "access_token": "<optional>" }

AnvilBase verifies the id-token’s signature, its audience (aud = the provider client_id you configured for the project), and the nonce when supplied, then find-or-creates + links the account and returns a project session (JWT pair) whose app_metadata.provider reflects the real provider.

With the SDK:

await db.auth.signInWithIdToken({ provider: "apple", token: appleIdToken /*, nonce */ })

Supported providers. The id-token grant is available only for providers with server-side id-token verification: apple, google, facebook, and microsoft. The provider must also be enabled (client id/secret configured) for the project. github, discord, twitter, and linkedin return 400 provider_id_token_not_supported — use the redirect flow above for those. A disabled provider returns 400 provider_not_enabled; an invalid/expired token returns 401 invalid_grant.

In the console

Project → Auth tab → Providers. Toggle each provider, paste the client id/secret, and adjust scopes. Secrets are stored masked and never re-displayed in full.

Tips

  • Use different OAuth apps per environment (dev/staging/prod) so callback URLs and secrets don’t bleed across deployments.
  • Always request the email scope (user:email, email) so AnvilBase can create/link the account by email.
  • Rotate the client secret at the provider and re-PUT it here if it leaks; the old secret stops working as soon as the provider invalidates it.

Next: Email & Templates.