# Authentication

## The token endpoint

`POST /api/v1/token`

The body may be JSON or form-encoded. `grant_type` defaults to `password` when
it is omitted.

| grant_type | required fields | result |
|---|---|---|
| `password` (client) | `username`, `password` | client token, scope `client` |
| `password` (app user) | `client_id`, `client_secret`, `username`, `password` | app user token, scope `app profile` |
| `client_credentials` | `client_id`, `client_secret` | app token, scope `app`, no user |
| `authorization_code` | `code`, `client_id`, `client_secret`, `redirect_uri` | app user token |

`client_id` in a token request is the app's `app_key`, not the client's `cid`.

## Client password grant

```bash
curl -sS -X POST "$API/token" \
  -H "Content-Type: application/json" \
  -d '{"grant_type":"password","username":"acme","password":"..."}'
```

The response has scope `client` and a `cid`.

## App user password grant

```bash
curl -sS -X POST "$API/token" \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "password",
    "client_id": "app_1a2b3c4d5e6f7a8b",
    "client_secret": "gsk_...",
    "username": "alice",
    "password": "..."
  }'
```

The app must be active and the app secret must match. When sub-apps are used,
app users are shared across the app family.

## Client credentials grant

```bash
curl -sS -X POST "$API/token" \
  -H "Content-Type: application/json" \
  -d '{"grant_type":"client_credentials","client_id":"app_...","client_secret":"gsk_..."}'
```

This returns an app token with scope `app` and no user. Use it for server to
server calls that act as the app itself.

## Authorization code grant

See [OAuth flow](oauth.md).

## Token response

```json
{
  "access_token": "cgt_...",
  "certified_gump_token": "cgt_...",
  "token_type": "Bearer",
  "expires_in": 43200,
  "scope": "app profile",
  "issued_at": 1761200000,
  "sub": "a_9k2m4x",
  "uid": "a_9k2m4x",
  "app": "app_1a2b3c4d5e6f7a8b",
  "app_username": "alice",
  "client": "c_4m2x9p"
}
```

`access_token` and `certified_gump_token` are the same value. `expires_in` is
authoritative. There is no refresh token.

## Sending a token

```bash
curl -sS "$API/userinfo" -H "Authorization: Bearer cgt_..."
```

The public API also accepts `X-Gauth-Token: cgt_...`.

A `gak_` app user key can be sent as a bearer token directly. It has no expiry
and is revoked individually.

## Identity

`GET /api/v1/userinfo` and `GET /api/v1/me` are aliases.

```json
{
  "active": true,
  "sub": "a_9k2m4x",
  "uid": "a_9k2m4x",
  "app_username": "alice",
  "email": "alice@example.com",
  "display_name": "Alice",
  "status": "active",
  "app": "app_1a2b3c4d5e6f7a8b",
  "app_name": "acme-app",
  "client": "c_4m2x9p",
  "scope": "app profile",
  "expires_at": 1761243200,
  "expires_in": 43199,
  "token_kind": "api"
}
```

A client token returns `cid` instead of `uid` and `app`.

## Client self-service

These require a client token.

```bash
curl -sS -X PATCH "$API/me" -H "Authorization: Bearer $CGT" \
  -H "Content-Type: application/json" \
  -d '{"email":"new@example.com","display_name":"New Name"}'

curl -sS -X POST "$API/me/password" -H "Authorization: Bearer $CGT" \
  -H "Content-Type: application/json" \
  -d '{"current_password":"old","new_password":"a-long-password"}'

curl -sS -X POST "$API/me/delete-request" -H "Authorization: Bearer $CGT" \
  -H "Content-Type: application/json" \
  -d '{"reason":"closing the project"}'
```

Changing a client password revokes every other session for that client.

## App user self-service

```bash
curl -sS -X POST "$API/apps/app_1a2b3c4d5e6f7a8b/password" \
  -H "Authorization: Bearer $CGT" \
  -H "Content-Type: application/json" \
  -d '{"current_password":"old","new_password":"a-long-password"}'
```

## My keys

```bash
curl -sS "$API/me/keys" -H "Authorization: Bearer $CGT"

curl -sS -X POST "$API/me/keys" -H "Authorization: Bearer $CGT" \
  -H "Content-Type: application/json" -d '{"label":"laptop"}'

curl -sS -X DELETE "$API/me/keys/7" -H "Authorization: Bearer $CGT"
```

The raw key is returned once, at creation. Afterwards only the hint is visible.

## Introspection

`POST /api/v1/introspect` requires client authentication: a client API key or
`client_id` plus `client_secret`.

```bash
curl -sS -X POST "$API/introspect" \
  -H "Content-Type: application/json" \
  -d '{"token":"cgt_...","client_id":"app_...","client_secret":"gsk_..."}'
```

An unknown, expired or revoked token returns `{"active": false}`.

## Revocation

`POST /api/v1/revoke` always returns 200, even for an unknown token.

```bash
curl -sS -X POST "$API/revoke" \
  -H "Content-Type: application/json" -d '{"token":"cgt_..."}'
```

## Health

`GET /api/v1/health` is unauthenticated.

```json
{"status":"ok","service":"gauth","issuer":"https://auth.gumpworks.com"}
```
