Actions
Actions — pre/post auth-event webhooks
Synchronous customer-hosted HTTPS hooks. Block, mutate, or augment auth events with your own server-side logic, no Authio-side code-upload required.
What you get
An Action is a customer-hosted HTTPS endpoint that Authio calls synchronously during an auth event. Authio POSTs a signed JSON envelope; your endpoint inspects it and returns a Verdict that:
- allows or denies the auth event (sign-in, sign-up, token-mint),
- on
pre_token_mintonly — overrides therolesandpermissionsJWT claims (Customer-as-source-of-truth, see the roles source-of-truth concept page), - appends arbitrary key/value metadata to the auth event’s audit-log entry.
Actions are NOT a sandboxed code runtime. Authio does not host your code. You write a small HTTP server somewhere you already operate (Lambda, Cloud Run, your existing API), and Authio calls it.
The six triggers
| Trigger | Fires | Can deny? | Can mutate JWT? |
|---|---|---|---|
pre_authenticate | Before primary factor (passkey / password / OAuth) is verified. | Yes | No |
post_authenticate | After primary factor, before a session row is created. | Yes | No |
pre_token_mint | Right before the access JWT is signed. | Yes | Yes — roles, permissions, plus arbitrary override_claims. |
post_token_mint | After the access JWT is signed, before the response is written. | No (informational) | No |
pre_register | At the top of every signup endpoint (passkey register, OAuth first-touch). | Yes | No |
post_register | After the user row is committed, before any welcome email fires. | No (informational) | No |
One action per (project, trigger)
At GA you may have at most one active action per trigger per project. Multi-action chaining is on the roadmap; for now, compose your logic inside a single endpoint per trigger.
Architecture in one picture
+----------------+ sign-in request +----------------+
| user / browser +------------------------------>+ auth-core |
+----------------+ +-------+--------+
|
| trigger=pre_token_mint
| POST /authio-action
| Authio-Signature: t=...,v1=...
| Content-Type: application/json
v
+------+---------+
| your server | (HTTPS, public)
+------+---------+
|
Verdict JSON
{ "decision": "allow",
"override_roles": [...] }
v
+------+---------+
| auth-core | applies verdict,
| JWT mint | signs access token
+------+---------+
|
v
200 OK + access_tokenConstraints (locked at GA)
- HTTPS only. Endpoint URLs must start with
https://. The dashboard rejects URLs that resolve to RFC 1918, link-local (169.254.x.x including AWS metadata), loopback, multicast, or any Authio-owned host. - Timeout 100ms–5000ms (default 2000ms). Slow endpoints get killed by the auto kill-switch.
- Response body ≤ 64 KiB. Oversize bodies are rejected as
invalid_response. - No redirects. 3xx responses fail closed at the transport layer.
- Synchronous only. No async / fire-and-forget mode. Your endpoint must respond within
timeout_msor the configuredfail_modeapplies.
Failure handling: fail_mode
Every action is configured with one of two failure modes that decide what happens when your endpoint is unreachable, slow, or returns invalid JSON:
fail_mode = open(default) — the auth event proceeds as if the action had returned allow. Best for analytics, role overrides where you’d rather ship Authio-default roles than block the sign-in, and any informational hook.fail_mode = closed— the auth event is denied with codeaction_unreachable. Best for compliance gates where missing the hook is unacceptable (geo-block, fraud-deny on a transient).
A verdict of decision: deny always blocks the auth event, regardless of fail_mode.
