---
title: 'REST API: Auth'
path: reference/rest-api/auth
status: published
---

# REST API: Auth

Runtime ScaiKey config, BFF token exchange, refresh, and the caller-identity endpoint.

All endpoints under `/api/v1/auth` (except `/v1/me` which is under `/v1`).

## `GET /v1/auth/config`

Returns the canvas's runtime configuration for OIDC sign-in. The browser fetches this before redirecting to the Authorization endpoint so it knows the right `client_id`, `audience`, etc.

**Response:**

```jsonc
{
  "auth_mode": "scaikey",                        // or "dev_token"
  "scaikey_base_url": "https://scaikey.scailabs.ai",
  "client_id": "rr1k00w8tbeej3xquqzklevs55zp6dp1",
  "audience": "rr1k00w8tbeej3xquqzklevs55zp6dp1",
  "redirect_uri": "https://scaiflow.example/auth/callback"
}
```

Public — no auth required. Surfaces only the public fields needed for PKCE redirection; the client_secret stays server-side.

## `POST /v1/auth/exchange`

BFF token exchange. The canvas posts the authorization code + PKCE verifier; the API adds the client_secret and forwards to ScaiKey's token endpoint.

**Body:**

```jsonc
{
  "code": "auth_code_from_authorize_redirect",
  "code_verifier": "the_pkce_verifier_the_browser_generated",
  "redirect_uri": "https://scaiflow.example/auth/callback"
}
```

**Response:**

```jsonc
{
  "access_token": "...",
  "refresh_token": "...",
  "id_token": "...",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

Errors:

- **400** — ScaiKey rejected the exchange (invalid grant, expired code, mismatched verifier). The detail forwards ScaiKey's `error` + `error_description`.
- **502** — ScaiKey upstream 5xx.
- **503** — ScaiKey OIDC not configured on the server (missing `SCAIKEY_BASE_URL` / `SCAIFLOW_SCAIKEY_CLIENT_ID` / `SCAIFLOW_SCAIKEY_CLIENT_SECRET`).

`redirect_uri` must match what was sent on the `/authorize` redirect — ScaiKey enforces this strict equality.

## `POST /v1/auth/refresh`

Refresh-token grant. Same proxy pattern — client_secret stays server-side.

**Body:**

```jsonc
{ "refresh_token": "..." }
```

**Response:** Same shape as `/v1/auth/exchange`.

Errors: same as exchange.

## `GET /v1/me`

The caller's principal, merged with their ScaiFlow-local admin role.

**Response:**

```jsonc
{
  "sub": "usr_xxx",
  "tenant_id": "tnt_acme",
  "tenant_slug": "acme",
  "partner_id": null,
  "email": "alice@acme.example",
  "name": "Alice",
  "display_name": "Alice Johnson",
  "admin_role": "super_admin"      // null | "tenant_admin" | "super_admin"
}
```

`admin_role` comes from the local mirror in the ScaiFlow DB — independent of ScaiKey roles. See [Concepts: Multi-tenancy and auth](../../concepts/multi-tenancy-and-auth#admin-roles-scaiflow-local).

Used by the canvas to decide what admin chrome to show (the toolbar dropdown's Admin link, the catalog publish form, the tenant flows list).
