AETHERLABS

OAuth Clients

Register an application, configure its redirect URIs, and manage its credentials.

OAuth Clients

An OAuth client is the record that lets an application sign users in through Aether Labs SSO. It holds a name, one or more redirect URIs, the scopes the app may request, and a secret.

You only need one for Pattern B. First-party apps on *.aetherlabs.agency use cookie SSO and need no client at all.

Create a client

From the SSO admin console at admin.sso.aetherlabs.agency → OAuth Clients, or with the API:

POST https://sso.aetherlabs.agency/api/admin/oauth-clients
Authorization: Bearer <admin access token>
Content-Type: application/json

{
  "name": "Your Product",
  "redirect_uris": ["https://yourapp.example.com/oauth/callback"],
  "scopes": ["openid", "profile", "email"],
  "is_active": true
}

Requires the oauth_clients:write permission. The response contains client_id and — once, never again — client_secret.

Redirect URIs

  • Matching is exact: scheme, host, port, path and query must all be identical to a registered value. No wildcards, no prefix or suffix matching.
  • Production callbacks must be https. http://localhost is accepted for local development.
  • Register every URI you will use — a production one and a dev one.
  • An unregistered URI is refused at /authorize with invalid_request; a mismatch at /token fails with invalid_grant.

Localhost URIs do not work in production

A client whose only redirect URI is http://localhost:… cannot complete the flow in production. Check this first if authorize returns invalid_request.

Manage an existing client

ActionEndpointPermission
List / readGET /api/admin/oauth-clients, GET /api/admin/oauth-clients/:clientIdoauth_clients:read
UpdatePUT /api/admin/oauth-clients/:clientIdoauth_clients:write
Rotate secretPOST /api/admin/oauth-clients/:clientId/rotate-secretoauth_clients:rotate_secret
DeactivateDELETE /api/admin/oauth-clients/:clientIdoauth_clients:delete

Rotating a secret immediately invalidates the old one: the app stops authenticating at /token until it is redeployed with the new value. Deactivating a client makes /authorize treat it as unknown.

Handling the secret

  • Stored bcrypt-hashed; the plaintext exists only in the creation/rotation response.
  • Keep it server-side. Never in a VITE_*, NEXT_PUBLIC_*, or any browser bundle.
  • The token and revocation endpoints authenticate the client with it, either as HTTP Basic (Authorization: Basic base64(client_id:client_secret)) or as client_id + client_secret in the request body.

Public clients are not supported

Every client is treated as confidential: a secret is required at the token endpoint. PKCE alone cannot stand in for it.

A browser-only app (SPA) therefore cannot hold the secret — it must run the authorization-code exchange on its own backend and issue its own session cookie (a BFF). That backend is also where the refresh token lives, which keeps it out of JavaScript entirely.

Next

On this page