<!-- section: Auth API · status: building · source: docs/auth-api/06-social-login.md -->

> Google and GitHub with your own OAuth app — configure, list, disconnect, and sign in.


# Social login

Google and GitHub, **with your own OAuth app**.

That last part is the design, not a limitation. The consent screen names the
application asking for access, so if we ran one app for the whole platform your
users would be told that *xplatform* wanted their email — from a company they
have never heard of, at the exact moment people abandon a sign-up. Your app,
your name on the screen.

## 1. Register this redirect URL with the provider

```
$BASE/v1/auth/callback/google
```

It points at us, because the token exchange happens here. Getting this wrong is
the most common way an OAuth setup fails, and it fails on the provider's site
where we cannot tell you anything useful — so the studio shows the exact string
to paste.

Match it character for character: no trailing slash, and the host has to be the
one your app is actually served from.

## PUT /v1/auth/providers/:provider

Give us the credentials. **Secret key**, from your server.

```bash Request
curl -X PUT "$BASE/v1/auth/providers/google" \
  -H "authorization: Bearer $SK" \
  -H "content-type: application/json" \
  -d '{"clientId":"…","clientSecret":"…"}'
```

`PUT` replaces the pair whole; there is no partial update, because a rotated
secret and a swapped app look identical from our side and a half-update would
let you believe you had changed something you had not.

## GET /v1/auth/providers

```bash Request
curl "$BASE/v1/auth/providers" -H "authorization: Bearer $SK"
```

```json Response
[
  {
    "provider": "google",
    "clientId": "123.apps.googleusercontent.com",
    "callbackUrl": "https://<your-deployment>/v1/auth/callback/google"
  }
]
```

**Client secrets are never returned.** They are encrypted at rest and there is no
endpoint that reads one back — you can replace a secret, not retrieve it.

`callbackUrl` is returned because it is the value people get wrong, and reading
it from the API beats copying it out of a doc.

## DELETE /v1/auth/providers/:provider

```bash Request
curl -X DELETE "$BASE/v1/auth/providers/google" -H "authorization: Bearer $SK"
```

**Accounts already linked through it are left alone.** Someone who signed up with
Google keeps their user record and everything attached to it. Disconnecting stops
new sign-ins; it does not delete people.

## POST /v1/auth/sign-in/social

Send people to the provider. **Publishable** key.

```bash Request
curl -X POST "$BASE/v1/auth/sign-in/social" \
  -H "authorization: Bearer $PK" \
  -H "content-type: application/json" \
  -d '{"provider":"google","callbackURL":"https://yourapp.com/welcome"}'
```

```json Response
{ "url": "https://accounts.google.com/o/oauth2/v2/auth?…", "redirect": true }
```

Redirect the browser there. They come back signed in, with the same session
cookie a password sign-in produces.

## When the address already has an account

If the same address already signed up with a password, the provider is linked to
that existing account rather than creating a second one — **but only once your
record of the address is verified.** Until then the callback fails with
`account_not_linked`.

[Verify email](/docs/auth-api/verify-email) explains why, and what to offer
someone who hits it.

## Errors

| Status | Code | Means |
|---|---|---|
| `404` | `PROVIDER_NOT_FOUND` | That provider is not configured for this project |
| `400` | `account_not_linked` | The address has an unverified account here |
| `400` | `state_mismatch` | The callback's state did not match; usually a stale tab |
| `403` | — | Publishable key used from an unregistered origin |

A provider you have not configured answers `404` rather than showing your users
a button that fails after they have left your site.
