Skip to Content
🔐 Faable AuthExtensibilityActions

Actions

Actions are small JavaScript functions that you write and Faable runs inside a sandbox at well-defined moments of the authentication pipeline. Use them to enforce business rules (e.g. block users without a verified email), require additional consent, or redirect users through your own UI before issuing tokens.

Actions are stored per-account, run in execution order, and have a focused API for the most common decisions: allow, deny, or pause-and-redirect.

Triggers

TriggerWhen it runsTypical use
post-loginAfter the user is authenticated, before tokens are issued.Enforce rules, redirect to custom UI (terms of service, MFA enrollment), enrich the session.
continueWhen the user returns from a post-login redirect via the /continue endpoint.Pick up state and decide what to do next.

Multiple actions can be registered at the same trigger; they execute in ascending order. The first one to call api.access.deny() short-circuits the chain; the first one to call api.redirect.sendUserTo() pauses the flow.

[!IMPORTANT] Plan limits: Hobby accounts can have 1 Action per account. Pro and Business allow unlimited Actions. See Auth pricing.

CRUD endpoints

MethodPathPurpose
POST/actionsCreate an action.
GET/actionsList actions (filterable by trigger).
POST/actions/:action_idUpdate name, code, order, or enabled flag.
DELETE/actions/:action_idDelete.

Fields

FieldDescription
nameFree-text label (max 200 chars).
triggerpost-login or continue.
codeThe JavaScript source to execute.
enabledWhen false, the action is skipped.
orderInteger; lower runs first.

Writing an Action

Your code must export an async function whose name matches the trigger. For post-login:

exports.onExecutePostLogin = async (event, api) => { // event holds context about the current login. // api is how you affect the outcome. };

The event object

event = { user: { user_id: string; email: string; email_verified: boolean; app_metadata: Record<string, unknown>; user_metadata: Record<string, unknown>; // …additional standard claims }; client: { client_id: string; name: string }; connection: { name: string; strategy: string }; request: { ip: string; userAgent: string }; };

The api object

api.access.deny(reason: string) // Reject the login. Returns HTTP 401 to the client with the given reason. // No further actions in the chain run. api.redirect.sendUserTo(url: string) // Pause the login flow and send the user's browser to `url`. // Faable persists state so the flow can resume via /continue once the // user returns.

Example — block users with an unverified email and require ToS acceptance

exports.onExecutePostLogin = async (event, api) => { if (!event.user.email_verified) { api.access.deny("Please verify your email before signing in."); return; } const tosAccepted = event.user.app_metadata?.tos_accepted_at; if (!tosAccepted) { api.redirect.sendUserTo("https://app.example.com/accept-tos"); } };

If the user clicks through your ToS page and you redirect them back to Faable’s /continue endpoint, a corresponding continue Action picks up — typically to mark the metadata field and let the flow proceed.

Console logging

console.log() and console.error() calls inside your code are captured and written to Logs, so you can debug behavior without exposing it to end users.

Execution mode

In production, Actions run on a dedicated worker process via RPC (so a misbehaving action can’t stall the auth server). The default timeout is 5 seconds per action. In local development, the auth server can also execute them in-process. This is an operational detail you don’t usually need to think about.

Next steps

  • Webhooks — react to events after they happen instead of intercepting them inline.
  • Logs — inspect Action execution and console.log output.
Last updated on