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 typed 402 plan_limit_exceeded (field maxApplications), 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 200 and writes no audit event.
  • Every UPDATE filters on tenant_id, client_id and client_type = 'server'.
  • Errors: 400 malformed_json, 400 validation_failed, 402 plan_limit_exceeded, 403 insufficient_role for viewers, 403 forbidden for another developer’s project and 404 not_found for a client that is not in this project. Player tokens and the server client’s own Basic credentials get 401.

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

  • DELETE on 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 limit 402, 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