Authentication#
Every API request carries an application key. A user token is added on top when the application acts on behalf of a signed-in person. The two apply together — they are not alternatives.
| What | Header | Who it is | Required |
|---|---|---|---|
| Application key | X-API-Key: <key> |
your application (partner integration, mobile app) | yes, on every request |
| User token | Authorization: Bearer <token> |
a signed-in person | no |
Without a token the call is evaluated as an anonymous visitor (role guest): the
application sees what an anonymous visitor of the website sees — public lists, articles and
adverts with masked personal data. With a token the call is evaluated with that user's roles,
within the roles allowed to the application.
# as an anonymous visitor
curl "https://your-site.com/api/blog/article/get-all?limit=5" \
-H "X-API-Key: $API_KEY"
# as a signed-in user
curl "https://your-site.com/api/blog/article/get-all?limit=5" \
-H "X-API-Key: $API_KEY" \
-H "Authorization: Bearer $TOKEN"
Application key#
The key is the X-API-Key header and is used over HTTPS only. It identifies the
application, not a user. It has the form coolcms_ followed by 64 hexadecimal
characters.
How to get a key#
- Ask the website administrator for a key for your application.
- The administrator creates the application in Settings → Applications with Api access and sets its allowed roles and limits. The steps for them are in Applications with Api access.
- They hand the key over to you. Store it where your application will read it from.
The full key is shown only once
The administrator sees it in full only when the application is created or a new key is issued. The system stores only a hash, so a lost key cannot be displayed again — only replaced with a new one. The administration shows just the first 16 characters to tell keys apart.
Disabling, another key, expiry and revoking#
An application can have several keys (at most 5 valid) and each key can have an expiry — a date and time when it stops working. The administrator can disable the application and enable it again, issue it another key, set or clear a key's expiry, revoke a key or revoke the whole application permanently. The change applies immediately:
| What the administrator does | What your application gets |
|---|---|
| disables the application | 401 with code 501 on every request (with any of its keys) until it is enabled again |
| issues another key | the existing keys keep working; you deploy the new key when it suits you |
| sets an expiry on a key | the key works until that moment, then returns 401 with code 501 |
| revokes a key | that key returns 401 with code 501 at once; the other keys of the application keep working |
| revokes the application | 401 with code 501 for good with all its keys; a revoked application cannot be enabled or given a key |
An expired key gets the same response as an unknown key. If an application that used to work
suddenly gets 401 with code 501, contact the website administrator. Request limits belong to the
whole application, not to a single key — all keys of the application share them.
Replacing the key#
A key can be replaced without an outage: the administrator sets an expiry on the old key, issues you a new key and you deploy it; both work until the expiry. Plan for this when you build the application:
- Read the key from configuration, not hard-coded — on a server from an environment variable or a configuration file, so that replacing the key does not need a new code deployment.
- A mobile app usually has the key in the build, so replacing it means a new version. Agree with the administrator on an expiry for the old key with enough margin for the app store review of the new version and for users to stop using the old one.
Handling the key#
- Server to server: keep the key secret on your server, out of the repository and out of logs. Do not send it to a browser or into a phone app.
- Mobile apps and browser scripts: a key shipped inside an app is not secret — anyone can extract it. On its own it therefore gives only anonymous-visitor access. The real boundary remains the signed-in user's rights.
- A leaked key gives a stranger only what an anonymous visitor of the website sees, and it uses up your application's limits. Ask the administrator for a new key and to revoke the leaked one — a revoked key stops working at once.
The key never widens a user's rights, it can only narrow them
Only the roles the administrator allowed to the application apply through it. A website
administrator who signs in through a partner application without the admin role has only
a regular user's rights there. See Roles allowed per application.
When the key is missing or invalid#
A missing, wrong, disabled, revoked or expired key always returns the same response — HTTP 401:
{"success":false,"message":"A valid application key (X-API-Key) is required.","data":null,"code":501}
The response intentionally does not say which case it was. Code 501 is API_UNAUTHORIZED.
Do not confuse it with 103 (the user token is invalid or expired) or with 400 (permission
denied, HTTP 403).
Endpoints that need no key#
Four addresses are called by third parties that cannot send a key. They do not require one:
| Address | Called by |
|---|---|
/api/system/payment/execute |
the payment gateway (notification and customer return) |
/api/system/payment/delivery |
the gateway or SMS aggregator — delivery notifications |
/api/system/payment/express-execute |
the aggregator of express SMS payments |
/api/system/auth/oauth-deauthorize-callback |
the sign-in provider — authorisation revoked |
Partner applications do not call them. Everything else, including sign-in, token refresh and sign-out, requires the key.
The website and administration need no key
They call the API internally, without HTTP. The key concerns external applications only.
JWT — user identity#
Sign-in, refresh and sign-out are ordinary API calls, so they carry the application key too.
| Endpoint | What it does |
|---|---|
POST /api/system/auth/login |
signs in, returns a token pair |
POST /api/system/auth/refresh |
refreshes the pair |
POST /api/system/auth/logout |
signs out |
GET /api/system/auth/me |
the signed-in user's profile |
GET/POST /api/system/auth/is-auth-valid |
verifies a token from the Authorization header or from the body (token) |
You sign in with a username or an e-mail
The field is called username but accepts both. A value shaped like an e-mail is
looked up among e-mails first, only then among usernames.
Sign-in via a third-party service has no HTTP wrapper
Google, Facebook and other providers are verified on the website, which checks the identity directly with the provider. No endpoint accepts a finished result from outside — a bare "provider and identifier" would be enough to take over someone else's account.
The request body#
Form encoding, not JSON
The authentication endpoints (login, refresh, logout) read only a form-encoded
body. A JSON body is not read.
The header#
An invalid token is not replaced by guest access
If Authorization is absent, the caller is a visitor. If the token is invalid, expired
or revoked, however, the API returns 401 with code 103, not a silent guest
access. The application must refresh the token or sign in again. The exception is the
public actions that do not read the token from the header (sign-in, token refresh,
forgotten password) — they are always evaluated as a visitor.
Lifetimes#
| Token | Lifetime |
|---|---|
| Access | 15 minutes |
| Refresh | 60 days, rotated |
Rotation and theft detection#
Every refresh returns a new pair. The old refresh token stops working.
Using an old refresh token invalidates the whole chain
The system treats it as theft and signs you out too. The response carries code
103. Always store the most recently returned pair.
When a token stops working early#
Signing out puts the access token on a deny list. Blocking or deactivating the account, a password change or reset (also by an administrator) and an account deletion request additionally invalidate all tokens issued to the account so far — access and refresh.
Your client must handle a 401 at any time
Not only after fifteen minutes. Signing out on another device, a password change or a blocked account invalidates the token at once.
A refused sign-in#
| Situation | Response |
|---|---|
| unknown account or wrong password | code 110 — both alike, so it cannot be learned which account exists |
| inactive account | code 109, only after a correct password |
| blocked account | code 104, only after a correct password |
| account pending deletion | code 117; data.scheduledAt carries the deletion date (other data items serve the website) |
| deleted account | code 110 — the original username and e-mail no longer lead to the account |
| too many failures | HTTP 429, see Limits |
A token refresh for an inactive, blocked or pending-deletion account returns code 102.
Account deletion is not started from the API
The request, confirmation and cancellation of an account deletion run on the website.
An account pending deletion cannot sign in during the grace period; a deleted
(anonymized) account cannot be saved — user/save returns 120.
Changing and resetting the password#
Passwords and keys only in the POST body, never in the URL
A URL ends up in server and proxy logs and stays in history. The actions below read passwords, keys and tokens only from the body (form or JSON); the same value in the URL is ignored and the action ends as if it were missing.
Changing the signed-in user's password#
POST /api/system/user/update-password with the Authorization header:
| Body field | Meaning |
|---|---|
currentPassword |
the current password — required |
password |
the new password, at least 8 characters |
| Result | Code |
|---|---|
| changed | 0; data carries a new pair token, refreshToken and payload |
| missing or wrong current password | 108 |
| new password too short | 611 |
| too many wrong attempts | 404 |
Store the new token pair after a password change
The change revokes every existing refresh token of the account, yours included. A client that does not store the new pair is signed out when its access token expires. Other devices must sign in again.
Forgotten password#
/api/system/user/send-new-password?username=…sends the account's e-mail a link to set a new password, valid for an hour. The password itself does not change and the response is always the same, whether the account exists or not.- The link leads to a website page. The password is set by
POST /api/system/user/reset-passwordwith the body fieldsemail,keyandpassword. An invalid, used or expired key and an unknown e-mail return one code,112; a short password611. On success every token of the account is revoked.
Verifying a token#
is-auth-valid takes the token from the Authorization: Bearer header, otherwise from the
body field token. A token in the URL is not read and the response is 103.
Failed attempts#
They are written to the audit log and to a table. The sign-in endpoint has a limit of 5 failed attempts per 5 minutes per account and address pair, and 20 failed attempts per hour per account from all addresses.