---
title: "Device Approve Endpoint"
method: POST
path: "/realms/public/protocol/openid-connect/device/approve"
tags: ["auth-keycloak"]
---

# Device Approve Endpoint

`POST /realms/public/protocol/openid-connect/device/approve`

Bind the consenting human to a pending device flow (RFC 8628 §3.3 consent).

Called SERVER-SIDE by the portal `/device` page after the signed-in human
types/confirms the `user_code` and clicks Allow — EXACTLY like /approve. Auth
is the SAME service-role delegation bridge: the portal presents the
service-role key + `X-Customer-User-Id`/`X-Customer-Org-Id` headers, which
`get_current_user` resolves (membership-checked by `_verify_delegation`,
suspension-checked by `_assert_org_active`) into a synthetic user-principal
AuthUser. The customer's Supabase JWT is NEVER forwarded — identity is
asserted via the trusted bridge.

Body: {user_code, action:'approve'|'deny'}. On approve the consenting
principal+org are bound to the row and status → 'authorized'; the token
endpoint later mints from those exact claims. On deny status → 'denied'.

NOTE: if the device-auth row carried a code_challenge, the CLI must hold the
verifier — the portal never sees it (PKCE is CLI <-> token-endpoint only).

## Request body

- object

## Response `200`

Successful Response

- unknown

## Other responses

- `401` — Unauthorized
- `403` — Forbidden
- `404` — Not Found
- `422` — Validation Error
- `500` — Internal Server Error

---

[API](https://skmtc.dev/geopera/apis/geopera-data-platform.md) · [All operations](https://skmtc.dev/geopera/apis/geopera-data-platform/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/geopera/geopera-data-platform/revisions/27294869ca25/schema)
