Administration

Managing applications

Create and configure OAuth 2.1 client applications in the admin interface or via the admin API.

Every OAuth 2.1 client you integrate with Auth Service is represented as an application record in the database. Applications hold the OIDC client configuration — redirect URIs, allowed scopes, MFA policy, and registration settings.

All application endpoints require an active session with the admin or superadmin role. Requests without a valid session cookie receive 401 AUTH_001; a session without one of those roles receives 403 AUTH_011.

The same operations are available in the admin interface (Applications, route /applications):

Create or edit in the admin interface

Create Application (and Edit Application on an existing one) opens a four-step form. Authorization and Advanced are optional: when creating, you can save from the Authorization step on; when editing, you can save from any step.

StepWhat you set
GeneralName, Slug (Client ID, locked when editing), Description, URL, Icon URL. When you type a URL, the form tries to detect the site's favicon for the icon and fills the first redirect URI with <URL>/callback while that field is empty
OptionsActive, Public client (PKCE only, no secret) (locked when editing), Allow registration, Require MFA, Skip consent screen, Hosted billing pages, Allow RP-Initiated Logout, and LiteLLM support (disabled unless the litellm and auth-service system applications exist)
AuthorizationAllowed scopes as toggles in two groups, OpenID Connect (openid, profile, email, phone, offline_access) and Circle claims (roles, permissions, features, org), each with a description of the claims it adds; Redirect URIs; Post-logout redirect URIs (shown only while Allow RP-Initiated Logout is on); Allowed resources
AdvancedBack-channel logout endpoint and, once a URI is entered, Require session id (sid); Social providers; Metadata (JWT client_attrs)

A new form starts with Allow registration on and with the scopes openid, profile, email, roles, permissions and features selected. The API default for allowRegister is false (see below), so applications created through the API without this field do not offer registration.

When a confidential application is created, the client secret is displayed once. Rotate Secret on the application detail page issues a new one. System applications are hidden from the list unless Show system applications is switched on.

Create an application

POST /api/admin/applications
Content-Type: application/json

{
  "name": "My App",
  "slug": "my-app",
  "description": "Optional description",
  "redirectUris": ["https://app.example.com/callback"],
  "allowedScopes": ["openid", "profile", "email", "roles", "permissions", "features"],
  "isActive": true,
  "skipConsent": false,
  "isMfaRequired": false,
  "allowRegister": false,
  "url": "https://app.example.com",
  "icon": "https://app.example.com/icon.png"
}
FieldTypeDefaultDescription
namestringrequiredDisplay name (1–100 chars)
slugstringrequiredURL-safe identifier used as client_id (lowercase alphanumeric + hyphens, 1–64 chars)
descriptionstring—Optional description (max 500 chars)
redirectUrisstring[]Allowed redirect URIs for the OAuth flow
allowedScopesstring["openid","profile","email"]Scopes this application may request
isActivebooleantrueWhether the application accepts new authorizations
skipConsentbooleanfalseSkip the consent screen for trusted first-party apps
isMfaRequiredbooleanfalseBlock token issuance for users without MFA enabled
allowRegisterbooleanfalseWhether the application's sign-in page offers registration, and whether a sign-up that names it (client_id) may create an account. See Registration
isPublicbooleanfalsetrue for a public client (browser or native app, PKCE only, no secret); false for a confidential client with a clientSecret. Set at creation, immutable afterwards
isSystembooleanfalseInfrastructure application (hidden from the admin list by default, cannot be deleted). Setting it requires the superadmin role (403 AUTH_011 otherwise)
billingMode"none" | "delegated""none"With delegated, the service serves the subscribe, invoices and billing-usage pages for the application. In the admin form it is the Hosted billing pages option of the Options step
urlstring—Application home URL. Also the application's resource identifier (RFC 8707 resource)
allowedResourcesstring[]Protected resources (URLs of other applications) this application may request tokens for. Unknown URLs are rejected with APP_001
enableEndSessionbooleanfalseForm label Allow RP-Initiated Logout. Lets the application call /api/auth/oauth2/end-session. The call ends the central SSO session for all applications (there is no per-application variant), and applications with a back-channel logout endpoint are notified. Leave it off for applications that only sign the user out locally
postLogoutRedirectUrisstring[] (seeded with <url>/ when url is set at creation and the list is empty)URLs accepted as post_logout_redirect_uri by end-session. Matching is strict, query string included. Each entry must be a valid URL. An empty list means no post-logout redirect is allowed
backchannelLogoutUristring | nullnullOIDC Back-Channel Logout 1.0 endpoint. Must be a public https:// URL with no credentials or fragment, whose host does not resolve to a loopback or private address (any other value is rejected with APP_001). When set, a session end POSTs a signed logout token here. See Back-channel logout
backchannelLogoutSessionRequiredbooleanfalseForm label Require session id (sid), shown only once a back-channel URI is entered. It records that the application needs the sid claim in every logout token and ends only the session it names (spec §2.8). The service includes sid in logout tokens regardless; this flag does not change that
enabledSocialProvidersstring | nullnullnull inherits the providers enabled on the server, [] disables social sign-in for this application, a list restricts it to those providers
metadataobject{}String key/value pairs (keys must match ^[a-zA-Z_][a-zA-Z0-9_]*$, values up to 256 characters). They are added to the application's tokens inside a client_attrs object. Keys that collide with reserved token claims (sub, aud, iss, exp, iat, nbf, jti, scope, scopes, azp, client_id, token_type, auth_time, acr, amr) are rejected
iconstring—Icon URL (displayed in admin and consent screen)

The 201 response contains the created application, the clientId (same as slug) and, for confidential applications, the clientSecret. The secret is shown only once — store it immediately. Public clients have no secret.

Creating an application also sets up:

  • two roles, user (the default role) and admin;
  • a default subscription plan named free;
  • access for every existing superadmin and for the creating admin, each with the admin role and the free plan.

Invalid data returns 400 APP_001; a slug that already exists returns 409 APP_003.

Logout settings

enableEndSession, postLogoutRedirectUris, backchannelLogoutUri and backchannelLogoutSessionRequired are stored on the OAuth client and returned in every application payload.

  • ID tokens carry the session id (sid) only when enableEndSession is on or a backchannelLogoutUri is set.
  • End-session ends the central session for all applications. No per-application end-session exists; revoking one user's access to one application (below) revokes that user's refresh tokens for that application only and does not end the shared session or send a back-channel logout.
  • Post-logout redirects are only accepted when the URL is listed in postLogoutRedirectUris.

List applications

GET /api/admin/applications

Returns { "applications": [...] }, ordered by creation date. Each entry is the application row plus its logout settings (enableEndSession, postLogoutRedirectUris, backchannelLogoutUri, backchannelLogoutSessionRequired).

Get a single application

GET /api/admin/applications/:id

Returns { "application": { ... } }, or 404 APP_002 when the id is unknown.

Update an application

PATCH /api/admin/applications/:id
Content-Type: application/json

{
  "isMfaRequired": true,
  "skipConsent": true
}

All fields from the create schema are accepted, except slug and isPublic, which are immutable after creation. Every field is optional and omitted fields keep their stored value. The response is { "application": { ... } }.

Rotate the client secret

POST /api/admin/applications/:id/rotate-secret

Generates a new random clientSecret, hashes it with SHA-256, and stores the hash. The plaintext secret is returned once, as { "clientSecret": "..." }. All tokens issued to the application with the previous secret are revoked. Public clients have no secret: the call returns 400 APP_001.

Delete an application

DELETE /api/admin/applications/:id

Returns 204 on success. The application's roles, plans, user access records and OAuth client are deleted with it, and its Stripe catalogue objects are archived rather than deleted. System applications (isSystem) cannot be deleted and return 403 APP_007. Unknown ids return 404 APP_002.

Grant a user access to an application

POST /api/admin/applications/:id/users
Content-Type: application/json

{
  "userId": "<user id>",
  "roleId": "<uuid>",             // optional — assigns this role; otherwise the default role is assigned
  "subscriptionPlanId": "<uuid>", // optional — otherwise the default plan is assigned
  "reason": "Onboarding"          // optional audit reason, 1–500 chars
}

Returns 201 { "ok": true }. If no roleId is given, the application's default role is assigned. If no subscriptionPlanId is given, the default subscription plan is assigned. A role that belongs to another application is rejected with APP_001; a plan that does not belong to this application is rejected with SUB_001.

Grant access in bulk

POST /api/admin/applications/:id/users/bulk
Content-Type: application/json

{
  "organizationId": "<organization id>"
}

Send exactly one of organizationId (every member of the organization) or userIds (1–1000 user ids). roleId, subscriptionPlanId and reason apply to every granted user. Users who already have access are skipped. The response is { "granted": <n>, "skipped": <n> }.

Update a user's access record

PATCH /api/admin/applications/:id/users/:userId
Content-Type: application/json

{
  "isActive": false,
  "roleId": "<uuid>",
  "subscriptionPlanId": "<uuid>",
  "reason": "Upgrade agreed with the customer"
}

roleId replaces the user's current role (null removes it). subscriptionPlanId (null revokes the subscription) and expiresAt (ISO 8601 date-time or null) are audited manual plan changes: a payload containing either one must also carry a reason, otherwise the request fails with 400 APP_001. Setting isActive to false also revokes the user's OAuth refresh tokens for this application. The response is { "ok": true }.

Revoke a user's access

DELETE /api/admin/applications/:id/users/:userId

Revokes the user's OAuth tokens for the application, then deletes the user's roles, subscription, consumption records and access record for it. Returns 204.

Copyright © 2026