# Auth · OAuth

> Signing in with a provider, and configuring the providers a deployment offers.

Signing in with a provider. `google` is the provider implemented today; the routes are written against a
provider list, so the others are configuration away rather than a rewrite.

## Render only what will work

`GET /api/auth/oauth` returns the providers with a client configured, and leaves the rest out entirely.
A sign-in page that renders from it shows exactly the buttons that will succeed, rather than a row of options that
fail on click.

## The flow

Navigate the browser to `/api/auth/oauth/:provider` — do not fetch it. The provider takes over, and sends
the person back to `/api/auth/oauth/:provider/exchange`, which trades the code for a session and sets the
cookies.

## Configuration

The `config` routes are admin-guarded and hold the client credentials. `config/validity` is
worth calling after any change: it reports whether the stored configuration would actually work, which is the
difference between "credentials are set" and "credentials are right" — a difference that otherwise only surfaces
when somebody tries to sign in.

## List configured providers

`GET /api/auth/oauth`

Guard: open

Every provider with a client configured, keyed by name. Providers with nothing configured are left out
entirely — so a sign-in page can render exactly the buttons that will work, rather than a row of options
that fail on click.

`google` is the provider implemented today.

### Parameters

- `provider` _string_ — Narrow to one provider. Query parameter.

### Returns

`{ configs }` keyed by provider, or `{ provider, config }` when one was named.

### Request (cURL)

```
curl https://example.com/api/auth/oauth
```

### Response

```json
{
  "configs": {
    "google": { "clientId": "1234.apps.googleusercontent.com", "redirectURI": "https://example.com/api/auth/oauth/google/exchange" }
  }
}
```

## Begin a provider's flow

`GET /api/auth/oauth/:provider`

Guard: open

Redirects the browser to the provider's consent screen. Navigate to it — do not fetch it — since the whole
point is to hand the person to the provider and get them back at
`/api/auth/oauth/:provider/exchange`.

A provider with no implementation answers `404` naming it.

### Parameters

- `provider` _enum_ **Required** — Path parameter.
  - One of: `google`

### Returns

A redirect to the provider.

### Request (cURL)

```
curl -i https://example.com/api/auth/oauth/google
```

### Response

```json
{
  "provider": "unsupported"
}
```

## Exchange the provider code for a session

`GET /api/auth/oauth/:provider/exchange`

Guard: open

The redirect target the provider sends the browser back to. Exchanges the authorization code for the
provider's tokens, resolves or creates the identity behind the account, and sets the session cookies.

This is a URL the provider calls, not one a client calls directly — it is documented so the redirect can be configured to match.

### Parameters

- `provider` _enum_ **Required** — Path parameter.
  - One of: `google`
- `code` _string_ **Required** — The authorization code, from the provider. Query parameter.
- `state` _string_ — The state the flow was started with. Query parameter.

### Returns

A redirect back into the site, with both session cookies set.

### Request (cURL)

```
curl -i "https://example.com/api/auth/oauth/google/exchange?code=4/0Ad…&state=…"
```

### Response

```json
{
  "identity": { "id": "01J2788XY98FDTA410C8S716CD", "kind": "member" }
}
```

## Read a provider's configuration

`GET /api/auth/oauth/:provider/config`

Guard: admin

The stored client configuration for one provider. An unimplemented provider answers `404` naming it.

### Parameters

- `provider` _enum_ **Required** — Path parameter.
  - One of: `google`

### Returns

`{ config }`.

### Request (cURL)

```
curl https://example.com/api/auth/oauth/google/config \
  -H "authorization: Bearer $TOKEN"
```

### Response

```json
{
  "config": {
    "clientId": "1234.apps.googleusercontent.com",
    "redirectURI": "https://example.com/api/auth/oauth/google/exchange"
  }
}
```

## Set a provider's configuration

`POST /api/auth/oauth/:provider/config`

Guard: admin

Writes the client credentials the flow runs with. A body that is not JSON answers `400`.

### Parameters

- `provider` _enum_ **Required** — Path parameter.
  - One of: `google`
- `clientId` _string_ **Required** — The provider's client id.
- `clientSecret` _string_ **Required** — The provider's client secret.
- `redirectURI` _string_ — Where the provider sends the browser back. Defaults to this deployment’s exchange route.

### Returns

`{ config }` as written.

### Request (cURL)

```
curl -X POST https://example.com/api/auth/oauth/google/config \
  -H "authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{ "clientId": "1234.apps.googleusercontent.com", "clientSecret": "…" }'
```

### Response

```json
{
  "config": {
    "clientId": "1234.apps.googleusercontent.com",
    "redirectURI": "https://example.com/api/auth/oauth/google/exchange"
  }
}
```

## Remove a provider's configuration

`DELETE /api/auth/oauth/:provider/config`

Guard: admin

Forgets the credentials. The provider then disappears from `GET /api/auth/oauth`, which is how a sign-in page stops offering it.

### Parameters

- `provider` _enum_ **Required** — Path parameter.
  - One of: `google`

### Returns

`200`.

### Request (cURL)

```
curl -X DELETE https://example.com/api/auth/oauth/google/config \
  -H "authorization: Bearer $TOKEN"
```

### Response

```json
{
  "ok": true
}
```

## Check whether a provider is usable

`GET /api/auth/oauth/:provider/config/validity`

Guard: admin

Whether the stored configuration would actually work — the difference between "credentials are set" and
"credentials are right", which otherwise only shows up when somebody tries to sign in.

### Parameters

- `provider` _enum_ **Required** — Path parameter.
  - One of: `google`

### Returns

`200` when valid; `409` with `{ issues }` when not.

### Request (cURL)

```
curl https://example.com/api/auth/oauth/google/config/validity \
  -H "authorization: Bearer $TOKEN"
```

### Response

```json
{
  "issues": ["redirectURI does not match the configured origin"]
}
```

## Check validity without the reasons

`HEAD /api/auth/oauth/:provider/config/validity`

Guard: admin

The same check with no body — `200` or `409`.

### Parameters

- `provider` _enum_ **Required** — Path parameter.
  - One of: `google`

### Returns

`200` when valid, `409` when not.

### Request (cURL)

```
curl -I https://example.com/api/auth/oauth/google/config/validity \
  -H "authorization: Bearer $TOKEN"
```

### Response

```json
{}
```
