Authentication
Axowl exposes several authentication mechanisms depending on the caller.
Route taxonomy
Section titled “Route taxonomy”| Prefix | Caller | Auth |
|---|---|---|
/api/org/{slug}/* | Dashboard (org member) | Session token; org + membership resolved by TenantResolutionMiddleware. |
/api/public/* | SDK / end user | Application key and/or per-org RS256 JWT. |
/api/m2m/token | Server-to-server | Exchanges an org API key (ah_live_…) for a short-lived token. |
/scim/v2/{orgSlug}/* | IdP provisioning | SCIM bearer token. |
/api/v1/admin/* | Platform admin | Session token. |
End-user tokens (RS256)
Section titled “End-user tokens (RS256)”End-user JWTs are signed with the org’s RSA key and verified via the org’s published JWKS. Server-authoritative checks (Introspect, CheckPermission) read fresh permissions from the database (IdentityServiceImpl.cs:226), so revocations apply immediately.
Sign in with an Axowl account (added 2026-09-26)
Section titled “Sign in with an Axowl account (added 2026-09-26)”An app group can offer Continue with Axowl account as a sign-in method, next to passkey, email and social. People use the Axowl account they already have; the app still receives its own end-user token.
- Turn it on in the dashboard: app group → Login methods → Sign in with Axowl account. It is always on while the app group is published on the marketplace.
GET /api/public/apps/{applicationKey}/login-methodsreports it asmethods.axowlAccount. - The flow runs on the hosted portal (
login.axowl.com/login/{applicationKey}/axowl), where the Axowl session cookie is readable. If the person is not signed in to Axowl, they go through the normal Axowl sign-in and come back. - The first time for an app group, the person sees what the app receives — name and email, nothing else — and must allow it. After that it is one click.
- The end-user account is matched within the app’s organization by email, the same rule as social sign-in; a new one is created with provider
Axowl. Only Axowl accounts with a verified email can use it. - The app gets the same token as any other method:
subis that organization’s end-user id. No Axowl account id, dashboard permission or other organization’s data is included.
| Method · Route | Purpose |
|---|---|
GET /api/v1/axowl-account/apps/{applicationKey} | What would be shared, whether consent is needed, whether the method is on. Needs the Axowl session. |
POST /api/v1/axowl-account/apps/{applicationKey}/connect | Body { "consent": true } the first time. Returns the normal sign-in response (or the MFA step). 409 CONSENT_REQUIRED without consent the first time · 403 AXOWL_ACCOUNT_LOGIN_OFF when the method is off. Cookie-authenticated, so it needs X-CSRF-Token. |
Apps installed by another organization (added 2026-09-27)
Section titled “Apps installed by another organization (added 2026-09-27)”An organization can install a marketplace app for its people: Install for my organization on the marketplace, sealed by an Owner or Admin. It is the same switch as turning on a marketplace service — POST /api/org/{slug}/integrations/app:{groupKey}/enable — and it only works while the app group is published and is not the installer’s own app (404 APP_NOT_ON_MARKETPLACE, 400 OWN_APP).
When someone signs in to the app with their Axowl account, the token carries tenant_orgs: the organizations they are an active member of that installed this app group, as [{ "slug", "name" }]. Use it to open the right company workspace. It is computed only from a sign-in proven by the Axowl account (never from an email match), and it is recomputed on every token: a person who leaves the company, or an install that is removed, drops out on the next token. The OIDC /token response carries the same claim.
Machine-to-machine
Section titled “Machine-to-machine”Use the API key directly. Org API keys are prefixed ah_live_ and presented as
Authorization: Bearer ah_live_… (IdentityServiceImpl.cs:124). They are SHA-256 hashed at rest,
scoped to a single organization, and can be rotated or revoked from Settings → Keys.
Token exchange (recommended for anything high-volume)
Section titled “Token exchange (recommended for anything high-volume)”POST /api/m2m/token trades the API key for a short-lived access token — 5 minutes by default.
Prefer it over sending the key on every call: the key is a long-lived secret, so each request that
carries it is another place it can leak (proxy logs, crash dumps, traces). The token expires on its
own and can be narrowed to fewer scopes than the key holds.
curl -X POST https://testapi.axowl.com/api/m2m/token \ -H 'Content-Type: application/json' \ -d '{"orgSlug":"acme","apiKey":"ah_live_…","scope":"org.member.read"}'{ "accessToken": "eyJhbGciOi…", "tokenType": "Bearer", "expiresIn": 300, "org": "acme" }Send it as Authorization: Bearer <accessToken> to /api/org/{slug}/*. The organization in the
token must match the {slug} in the route, and if the key is bound to a service account, that
account must still be active — both are re-checked on every request, so suspending the service
account takes effect immediately rather than when the token expires. Revoking the key also cuts
off tokens already issued from it, on the next request.
scope is optional and can only narrow what the key already has; it never widens it.
Longer-lived tokens for devices (added 2026-09-12)
Section titled “Longer-lived tokens for devices (added 2026-09-12)”Robots, gateways and other IoT devices lose connectivity and should not have to re-exchange every five minutes. The lifetime is a per-key ceiling, not an organization setting, so widening it for one device fleet changes nothing for your other keys.
- When issuing the key (
POST /api/org/{slug}/settings/keys/api-keys, or Settings → Keys), setmaxTokenLifetimeSeconds. Allowed values: 60 seconds to 7 days (604800). Anything above the default 300 requires the request to come from the organization owner and to carry the owner’s personal seal (assertionResponseJson, a passkey assertion bound toapi_key.long_lifetime). The dashboard collects the seal for you when you pick 1 hour, 24 hours or 7 days. Shorter-than-default ceilings need neither. A key’s ceiling cannot be changed afterwards — revoke and reissue. - When exchanging, pass
lifetimeSeconds. The issued lifetime ismin(lifetimeSeconds, key ceiling, 7 days), and the response’sexpiresInis the real value. OmittinglifetimeSecondsalways yields the 5-minute default, even on a 7-day key — a longer token is something the device asks for explicitly.
curl -X POST https://testapi.axowl.com/api/m2m/token \ -H 'Content-Type: application/json' \ -d '{"orgSlug":"acme","apiKey":"ah_live_…","lifetimeSeconds":86400}'| Request | Key ceiling | Issued |
|---|---|---|
| omitted | any | 300 |
86400 | none (default key) | 300 |
86400 | 86400 | 86400 |
604800 | 86400 | 86400 |
30 | any | 60 (floor) |
Revocation still wins: every /api/org/{slug}/* request checks the issuing key against a revocation
marker that is kept for the key’s full ceiling plus one hour, so a revoked 7-day key is refused on
the next call for the whole window. What the ceiling does widen is exposure — a leaked token is a
bearer credential for its entire lifetime until you revoke the key — which is why issuing one is an
owner-plus-seal action. Keep ceilings as short as the device’s offline pattern actually needs.