Applications
Machine clients and their secrets.
An application is a machine client of ErtisAuth: a backend service, a worker, a server-side web application. Applications don't sign in; they send a Basic token with every request:
Authorization: Basic <application_id>:<secret>Like users, applications have a role and may have permissions of their own (permissions, forbidden), evaluated as described in Authorization.
Typical uses:
- a backend that manages users on behalf of your product (sign-up, profile pages, admin screens),
- the server side of your password reset and activation pages, which call ErtisAuth with the application's token,
- services protected by the SDK that call each other.
The application object#
{
"_id": "66f1c0d2a4b5c6d7e8f90126",
"name": "Backend",
"slug": "backend",
"role": "backend-service",
"permissions": [ "users.create" ],
"forbidden": [],
"membership_id": "66f1c0d2a4b5c6d7e8f90123",
"sys": { "created_at": "2026-01-01T12:00:00Z", "created_by": "admin" }
}| Field | Required | Description |
|---|---|---|
name | yes | Display name. |
slug | no | Unique in the membership; derived from the name when omitted. |
role | yes | The slug of an existing role. |
permissions, forbidden | no | UBAC entries of the application (resource.action.object). |
The secret is never part of the application object.
The secret#
- A secret is generated when the application is created and returned only in that response.
- ErtisAuth stores only a hash of it. Nobody, administrators included, can read it back.
- It doesn't expire. Replace it with rotate the secret when it may have leaked, or as a routine.
Store the secret like a password: in a secret store or an environment variable, never in source control or in a browser.
Endpoints#
All routes are under /memberships/{membershipId}.
| Method | Route | Description | Permission |
|---|---|---|---|
GET | /applications/{id} | Get an application (id or slug) | applications.read.{id} |
GET | /applications | List applications | applications.read |
POST | /applications/_query | Query applications | applications.read |
GET | /applications/search?keyword= | Search applications | applications.read |
POST | /applications | Create an application | applications.create |
PUT | /applications/{id} | Update an application | applications.update.{id} |
POST | /applications/{id}/secret | Rotate the secret | applications.update.{id} |
DELETE | /applications/{id} | Delete an application | applications.delete.{id} |
DELETE | /applications | Delete several applications | applications.delete |
An application can always read and update its own record, unless its role forbids it.
Create an application#
curl -X POST https://auth.example.com/memberships/<membership_id>/applications \
-H 'Authorization: Bearer <access_token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "Billing Service",
"role": "billing"
}'Response 201 Created
{
"_id": "66f1c0d2a4b5c6d7e8f90140",
"name": "Billing Service",
"slug": "billing-service",
"role": "billing",
"membership_id": "66f1c0d2a4b5c6d7e8f90123",
"secret": "<application_secret>",
"sys": { "created_at": "2026-01-01T12:00:00Z", "created_by": "admin" }
}Save secret now: it can't be read again.
| Error | When |
|---|---|
400 ModelValidationError | Missing name or role, unknown role, invalid slug, or the same expression in both lists |
404 MembershipNotFound | The membership does not exist |
409 ApplicationAlreadyExists | The slug is taken |
Update an application#
PUT /memberships/{membershipId}/applications/{id}Updates name, slug, role, permissions and forbidden. The secret is not changed here. An update without any change answers 409 IdenticalDocumentError.
Rotate the secret#
POST /memberships/{membershipId}/applications/{id}/secret
Authorization: Bearer <access_token>Response 200 OK: the application with its new secret.
BasicTokenCacheTTL.Delete an application#
DELETE /memberships/{membershipId}/applications/{id}Response 204 No Content. Its Basic token stops working.
Using the Basic token#
curl https://auth.example.com/memberships/<membership_id>/users?limit=10 \
-H 'Authorization: Basic 66f1c0d2a4b5c6d7e8f90140:<application_secret>'- The token is
id:secretas plain text, not base64-encoded. GET /mewith a Basic token returns the application.- An unknown application, a wrong secret and an unknown membership are all answered with
401 InvalidToken.
Migrating legacy applications#
Applications created before per-application secrets existed authenticated with the membership's secret key as their secret. To keep them working during a migration, a membership can temporarily accept the membership secret for applications that don't have a secret of their own yet:
{ "allow_membership_secret_for_applications": true }This field is part of the membership update request. Migrate each application by rotating its secret and deploying the new one; from then on only its own secret works. When all applications are migrated, set the switch to false.
Events#
ApplicationCreated, ApplicationUpdated and ApplicationDeleted. See Events.
Found a mistake in the docs? Open an issue