AUTH4D-305 Renaming and turning off server clients
Server clients (AUTH4D-207 (repository file docs/api-spec/auth4d-207.md)) can now be renamed and turned off and on without
deleting them. A server client that is off keeps its client ID and its secret hashes, but every
client authentication with it is refused, so introspection (AUTH4D-155 (repository file docs/api-spec/auth4d-155.md)) answers
401 invalid_client for both the current secret and a previous secret that is still inside its
overlap window. Turning it on again makes the same secrets work, with no redeploy.
Update a server client
PATCH /management/tenants/{tenantId}/clients/{clientId} (scope client:write; PUT behaves the
same)
{ "displayName": "Lobby server", "enabled": false }
| Field | Required | Notes |
|---|---|---|
displayName |
No | 1–80 characters after trimming |
enabled |
No | false turns the client off, true turns it back on |
The body is UpdateServerClientRequestSchema in packages/contracts, which is strict: any other
field, including clientType, redirectUris, allowedScopes, requireVerifiedEmail, branding,
idempotencyKey and the reserved credential fields, gets 400 validation_failed and changes
nothing. Browser and native applications keep their existing PATCH body.
200 with Cache-Control: no-store and the same document as GET on the client: client (the
ClientPolicy, with the new displayName and enabled) plus secret_status. No response holds a
secret or a hash.
- Turning a client on uses an application slot, exactly like creating one. The plan check runs
before the write and again inside the
UPDATE, so a concurrent change can’t go over the plan. Over the limit the answer is the typed402 plan_limit_exceeded(fieldmaxApplications), and the client stays off. A client that is off doesn’t count towards the limit, and renaming it needs no slot. - A PATCH that changes nothing returns
200and writes no audit event. - Every
UPDATEfilters ontenant_id,client_idandclient_type = 'server'. - Errors:
400 malformed_json,400 validation_failed,402 plan_limit_exceeded,403 insufficient_rolefor viewers,403 forbiddenfor another developer’s project and404 not_foundfor a client that is not in this project. Player tokens and the server client’s own Basic credentials get401.
Audit
One event per change, in the same D1 batch as the UPDATE:
| Change | Event | Metadata |
|---|---|---|
| Turned off | management.client.disabled |
client_type: "server", changed_fields, status: "disabled" |
| Renamed, turned on, or both | management.client.updated |
client_type: "server", changed_fields, status after the change |
changed_fields lists field names only (displayName, enabled), never the new name or any
secret material.
Unchanged
DELETEon a server client still removes it and its secrets for good.- Its logo routes still answer
403 reserved_client. - Secret rotation works whether the client is on or off.
Console
The server client screen shows On or Off next to its name, a Server client name form and a Turn off / Turn on button with a confirmation dialog. The dialog says that servers using the client are refused introspection while it is off and that its secrets are kept. The applications list shows a server client that is off as Off. Viewers see the status without the controls. The BFF forwards the PATCH like any other management write.
Tests
tests/integration/auth4d-305.server-client-toggle.test.ts: rename, turning off (current and overlapping previous secrets refused at/oauth/introspect, hashes kept), turning on, the plan limit402, extra fields, other projects, viewers, audit rows and permanent deletion.tests/e2e/auth4d-305.server-client-toggle.spec.ts(auth4d-305.playwright.config.ts, port 5305): rename and turn off and on at 320px and 1280px, the list status, the plan limit message and viewer access.
Source: docs/api-spec/auth4d-305.md