Eternaltwin

Home | /api | v1

/api/v1/oauth_clients

Seeding of the OAuth clients declared in the server configuration. Defined in crates/rest/src/oauth_clients.rs.

Internal. This route is not authenticated with a user credential at all: it requires the Etwin-Internal-Auth header, whose value is the internal key of the running instance. It exists so that the boot sequence can register the clients of [seed.app] against a backend that is already serving, and it is not part of the surface an application may use.

The path is oauth_clients, with an underscore, as it is mounted in crates/rest/src/lib.rs.

An application that wants to use OAuth should read Eternaltwin for OAuth; the browser flow itself is listed under Internal endpoints.

POST /

Creates the client, or updates it in place when one already exists under the same key or id. Answers with the stored client.

Headers

HeaderValue
Etwin-Internal-AuthThe instance's internal authentication key.

Body

FieldTypeMeaning
idUUID or nullPin the client to this identifier. Omit to let the server allocate one.
keystringStable handle, ^[a-z_][a-z0-9_]{1,31}@clients$.
display_namestringShown on the consent screen, ^[A-Za-z_ ()-]{2,32}$.
app_uriURLHomepage of the application.
callback_uriURLWhere an authorization is redirected back to.
secretstringClient secret, as lowercase hexadecimal of its UTF-8 bytes.
allowed_scopesobject or absentThe scopes this client may request.

allowed_scopes is an object of four booleans, base, forum_read, forum_write and forum_moderate, not the space-separated string of the OAuth scope parameter.

Omitting allowed_scopes leaves the client's current allowance alone, and on an insert stores no allowance at all — read back as base alone. An upsert that does not mention scopes never widens what a client may ask for.

Example

POST /api/v1/oauth_clients
Content-Type: application/json
Etwin-Internal-Auth: dev

{
  "id": "e1484871-29a7-41b1-8c3f-0aa334be1c40",
  "key": "alpha_dev@clients",
  "display_name": "alpha",
  "app_uri": "http://alpha.localhost/",
  "callback_uri": "http://alpha.localhost/oauth/callback",
  "secret": "646576",
  "allowed_scopes": {
    "base": true,
    "forum_read": true,
    "forum_write": true,
    "forum_moderate": false
  }
}
{
  "type": "OauthClient",
  "id": "e1484871-29a7-41b1-8c3f-0aa334be1c40",
  "key": "alpha_dev@clients",
  "display_name": "alpha",
  "app_uri": "http://alpha.localhost/",
  "callback_uri": "http://alpha.localhost/oauth/callback",
  "owner": null
}

The response never carries the secret, hashed or otherwise, and owner is null for a system client: it belongs to the instance rather than to a member.

Errors

StatusBodyWhen
401{"error": "missing internal authentication header"}No Etwin-Internal-Auth.
403{"error": "invalid internal authentication key"}Wrong key.
500{"error": "internal error"}The store refused the upsert.