# OAuth

### Describe an in-flight OAuth login request for the console login screen

`GET /oauth/login-request`

**Parameters**

- `challenge` _(query)_ · `string` · required — The Hydra login_challenge from the authorize redirect.

**Returns**

- `200` · `OauthLoginRequestViewDto`
- `422` · `ErrorEnvelopeDto` — validation_failed (expired/used challenge)
- `429` · `ErrorEnvelopeDto` — rate_limited

Attributes of `OauthLoginRequestViewDto`:

- `client` · `object` · read-only — The OAuth client requesting access (self-declared metadata — show, never trust).
- `requestedScope` · `string[]` · read-only — The OAuth scopes the client asked for (informational — authority comes from consent).

**Example request**

```bash
curl https://api.koo.io/oauth/login-request?challenge=example \
  -H "Authorization: Bearer kc_your_api_token"
```

**Example response** (`200`)

```json
{
  "client": {
    "clientId": "client_01example0000000000000000x",
    "clientName": "example",
    "clientUri": "example",
    "logoUri": "example"
  },
  "requestedScope": [
    "example"
  ]
}
```

### Accept an OAuth login request as the signed-in user (subject-bound)

`POST /oauth/login/complete`

**Request body**

- `loginChallenge` · `string` · required — The Hydra login challenge being accepted.

**Returns**

- `200` · `OauthRedirectDto`
- `401` · `ErrorEnvelopeDto` — unauthenticated
- `403` · `ErrorEnvelopeDto` — forbidden (machine actor / subject mismatch)
- `422` · `ErrorEnvelopeDto` — validation_failed (expired/used challenge)

Attributes of `OauthRedirectDto`:

- `redirectTo` · `string <uri>` · read-only — Where to navigate next to continue the OAuth flow.

**Example request**

```bash
curl https://api.koo.io/oauth/login/complete \
  -X POST \
  -H "Authorization: Bearer kc_your_api_token" \
  -H "Content-Type: application/json" \
  -d '{
      "loginChallenge": "example"
    }'
```

**Example response** (`200`)

```json
{
  "redirectTo": "https://example.com"
}
```

### Reject (cancel) an OAuth login request — the client receives access_denied

`POST /oauth/login/reject`

**Request body**

- `loginChallenge` · `string` · required — The Hydra login challenge being rejected.

**Returns**

- `200` · `OauthRedirectDto`
- `422` · `ErrorEnvelopeDto` — validation_failed (expired/used challenge)

Attributes of `OauthRedirectDto`:

- `redirectTo` · `string <uri>` · read-only — Where to navigate next to continue the OAuth flow.

**Example request**

```bash
curl https://api.koo.io/oauth/login/reject \
  -X POST \
  -H "Authorization: Bearer kc_your_api_token" \
  -H "Content-Type: application/json" \
  -d '{
      "loginChallenge": "example"
    }'
```

**Example response** (`200`)

```json
{
  "redirectTo": "https://example.com"
}
```

### Describe an in-flight OAuth consent request: client + the signed-in user’s grantable accounts

`GET /oauth/consent-request`

**Parameters**

- `challenge` _(query)_ · `string` · required — The Hydra consent_challenge from the authorize redirect.

**Returns**

- `200` · `OauthConsentRequestViewDto`
- `401` · `ErrorEnvelopeDto` — unauthenticated
- `403` · `ErrorEnvelopeDto` — forbidden (machine actor / subject mismatch)
- `422` · `ErrorEnvelopeDto` — validation_failed (expired/used challenge)

Attributes of `OauthConsentRequestViewDto`:

- `client` · `object` · read-only — The OAuth client requesting access (self-declared metadata — show, never trust).
- `requestedScope` · `string[]` · read-only — The OAuth scopes the client asked for (informational — authority comes from consent).
- `requestedAudience` · `string[]` · read-only — The access-token audience(s) the client requested (the MCP resource URLs).
- `accounts` · `object[]` · read-only — The signed-in user’s accounts — consent grants access to exactly ONE of them.

**Example request**

```bash
curl https://api.koo.io/oauth/consent-request?challenge=example \
  -H "Authorization: Bearer kc_your_api_token"
```

**Example response** (`200`)

```json
{
  "client": {
    "clientId": "client_01example0000000000000000x",
    "clientName": "example",
    "clientUri": "example",
    "logoUri": "example"
  },
  "requestedScope": [
    "example"
  ],
  "requestedAudience": [
    "example"
  ],
  "accounts": [
    {
      "id": "acct_01example0000000000000000x",
      "handle": "alice",
      "displayName": "My app",
      "myRole": "owner",
      "grantableRoles": [
        "reader"
      ]
    }
  ]
}
```

### Grant the client access to ONE account at a capped machine role (reader/editor)

`POST /oauth/consent/complete`

**Request body**

- `consentChallenge` · `string` · required — The Hydra consent challenge being accepted.
- `accountId` · `string` · required — The single account this grant confers access to.
- `role` · `"reader" | "editor"` · required — Token role: `editor` (Write) can deploy and manage services; `reader` (Read) is view-only.

**Returns**

- `200` · `OauthRedirectDto`
- `401` · `ErrorEnvelopeDto` — unauthenticated
- `403` · `ErrorEnvelopeDto` — forbidden (machine actor / subject mismatch / not a member / role above membership)
- `422` · `ErrorEnvelopeDto` — validation_failed (expired/used challenge)

Attributes of `OauthRedirectDto`:

- `redirectTo` · `string <uri>` · read-only — Where to navigate next to continue the OAuth flow.

**Example request**

```bash
curl https://api.koo.io/oauth/consent/complete \
  -X POST \
  -H "Authorization: Bearer kc_your_api_token" \
  -H "Content-Type: application/json" \
  -d '{
      "consentChallenge": "example",
      "accountId": "acct_01example0000000000000000x",
      "role": "reader"
    }'
```

**Example response** (`200`)

```json
{
  "redirectTo": "https://example.com"
}
```

### Reject (deny) an OAuth consent request — the client receives access_denied

`POST /oauth/consent/reject`

**Request body**

- `consentChallenge` · `string` · required — The Hydra consent challenge being rejected.

**Returns**

- `200` · `OauthRedirectDto`
- `422` · `ErrorEnvelopeDto` — validation_failed (expired/used challenge)

Attributes of `OauthRedirectDto`:

- `redirectTo` · `string <uri>` · read-only — Where to navigate next to continue the OAuth flow.

**Example request**

```bash
curl https://api.koo.io/oauth/consent/reject \
  -X POST \
  -H "Authorization: Bearer kc_your_api_token" \
  -H "Content-Type: application/json" \
  -d '{
      "consentChallenge": "example"
    }'
```

**Example response** (`200`)

```json
{
  "redirectTo": "https://example.com"
}
```

### The MCP clients you have authorized (from Hydra’s consent sessions)

`GET /me/mcp-grants`

**Returns**

- `200` · `McpGrantDto[]`
- `401` · `ErrorEnvelopeDto` — unauthenticated
- `403` · `ErrorEnvelopeDto` — forbidden (machine actor)

**Example request**

```bash
curl https://api.koo.io/me/mcp-grants \
  -H "Authorization: Bearer kc_your_api_token"
```

**Example response** (`200`)

```json
[
  {
    "clientId": "client_01example0000000000000000x",
    "clientName": "example",
    "accountId": "acct_01example0000000000000000x",
    "accountHandle": "example",
    "role": "reader",
    "grantedAt": "2026-01-01T00:00:00.000Z"
  }
]
```

### Revoke an MCP client’s grant — new requests stop immediately; issued tokens expire within 1 hour

`DELETE /me/mcp-grants/{clientId}`

**Parameters**

- `clientId` _(path)_ · `string` · required — The authorized OAuth client id to revoke.

**Returns**

- `204` — Revoked (idempotent)
- `401` · `ErrorEnvelopeDto` — unauthenticated
- `403` · `ErrorEnvelopeDto` — forbidden (machine actor)

**Example request**

```bash
curl https://api.koo.io/me/mcp-grants/:clientId \
  -X DELETE \
  -H "Authorization: Bearer kc_your_api_token"
```

### RFC 7591 dynamic client registration (hardened proxy in front of Hydra)

`POST /oauth/register`

**Returns**

- `201` — The registered client document (RFC 7591 JSON, sanitized)
- `400` — RFC 7591 error JSON (invalid_redirect_uri / invalid_client_metadata)

**Example request**

```bash
curl https://api.koo.io/oauth/register \
  -X POST \
  -H "Authorization: Bearer kc_your_api_token" \
  -H "Content-Type: application/json" \
  -d '{
      "key": "example"
    }'
```

### RFC 7592 client read — not offered (this server issues no registration access tokens)

`GET /oauth/register/{clientId}`

**Parameters**

- `clientId` _(path)_ · `string` · required — The registered OAuth client id.

**Returns**

- `404` — RFC error JSON — client management is not available

**Example request**

```bash
curl https://api.koo.io/oauth/register/:clientId \
  -H "Authorization: Bearer kc_your_api_token"
```

### RFC 7592 client update — not offered (this server issues no registration access tokens)

`PUT /oauth/register/{clientId}`

**Parameters**

- `clientId` _(path)_ · `string` · required — The registered OAuth client id.

**Returns**

- `404` — RFC error JSON — client management is not available

**Example request**

```bash
curl https://api.koo.io/oauth/register/:clientId \
  -X PUT \
  -H "Authorization: Bearer kc_your_api_token" \
  -H "Content-Type: application/json" \
  -d '{
      "key": "example"
    }'
```

### RFC 7592 client deprovision — not offered (this server issues no registration access tokens)

`DELETE /oauth/register/{clientId}`

**Parameters**

- `clientId` _(path)_ · `string` · required — The registered OAuth client id.

**Returns**

- `404` — RFC error JSON — client management is not available

**Example request**

```bash
curl https://api.koo.io/oauth/register/:clientId \
  -X DELETE \
  -H "Authorization: Bearer kc_your_api_token"
```
