Flows

A flow is a short list of steps that runs at a fixed point in authentication or account management. It can refuse an action, ask a player to accept your terms at sign-in, or store a role or metadata. Projects with no flows keep the existing behavior.

Where flows run

Each flow belongs to one trigger, a fixed point in authentication or account management. auth4.dev finishes its protocol checks first, such as PKCE, the redirect URI and rate limits. A flow runs after those checks and can't change them.

Console nameAPI nameRunsIf the flow denies
Before registrationpre_registrationBefore a new player account is stored. There is no user yet.No account is created.
After sign-inpost_loginAfter the player has signed in, before any code or token is issued.The app gets access_denied; guests get status 403.
Before guest upgradepre_guest_upgradeBefore an email address or social account is attached to a guest account.Nothing is attached, and the guest stays signed in as a guest.
Device approvaldevice_approvalWhen a signed-in browser approves a game's device code.The game's next poll gets access_denied.
Before token refreshpre_refreshAfter the refresh token and session are validated, before the token rotates.The old refresh token remains unconsumed; the client gets invalid_grant.
After sign-outpost_sign_outAfter a session is revoked by sign-out or access-token revocation.It cannot undo sign-out. Step failures are recorded and the session stays revoked.
Before account deletionpre_account_deletionBefore the project queues a user's deletion request.The request is refused; the user is not disabled and no job is queued.

Every flow is limited to 16 steps and 2 seconds. Refresh flows allow only deny andcondition; conditions can use only request context and identifiers, so they make no network or identity-data lookup. Refresh step errors and timeouts always fail closed. Sign-out flows allow only call_backend; its answer is recorded but never changes the completed sign-out. Account-deletion flows run at project scope and allow deny,condition, require_verified_email, allow_list,block_list and call_backend. Other step types are rejected at publish.

A new account signs in straight after it's created, so a sign-up runs pre_registration and then post_login. Steps that need a player, such as storing a role, can't run before registration.

Players never see your reason codes. Hosted login shows one of a few fixed messages, which you choose per step; see denial messages.

Which flow runs

For each trigger, auth4.dev runs one flow:

  1. the flow the application overrides the trigger with, if it has one;
  2. otherwise the flow the project uses for that trigger;
  3. otherwise the built-in default, which has no steps: the action continues and no trace is written.

The application is the client ID in the request. Account deletion has no application context and runs only a project binding. Use an application override for other triggers when, for example, a beta build should only let your testers in while the released game stays open.

In the console, open Settings in a project and select Open the flows storyboard. The storyboard shows each trigger with the steps that run there and lists the applications that override it. Owners and admins choose Use for project on a published flow, orUse the built-in default to stop running one. On an application's page, the Flows section offers Inherit from project or Override for each trigger.

What a flow holds

A flow is a list of up to 16 steps, run in order, and a time budget for the whole flow. The console edits it as step cards; the management API stores it as JSON:

A flow definition
{
  "timeBudgetMs": 1500,
  "steps": [
    { "id": "verified", "type": "require_verified_email" },
    {
      "id": "banned",
      "type": "block_list",
      "attribute": "steam_id",
      "values": ["76561197960287930"],
      "message": "account_restricted"
    },
    { "id": "player", "type": "set_role", "role": "player", "onError": "fail_open" }
  ]
}
  • id names the step in traces. Step IDs are unique in the flow and use lowercase letters, digits, _ and -.
  • when (Run only when (optional) in the console) limits a step to some sign-in methods or countries. Without it, the step runs for every sign-in.
  • timeBudgetMs (Time budget (ms)) is 50 to 2,000 ms, default 2,000.
  • onError (If the step fails or times out) decides what happens when a step can't finish: fail_closed, the default, denies the sign-in, and fail_open passes over the step. Once the time budget is spent, every remaining step counts as timed out.

A denial stops the flow, and later steps don't run. Roles and metadata that steps store are written only when the whole flow continues, so a later denial leaves nothing behind. Each step is described in the step reference.

Condition facts

A condition can use is_first_sign_in in an after-sign-in flow. It is true when the sign-in created the account, including a new guest account, and false for later sign-ins. Conditions on other triggers cannot use this fact.

Conditions can also read one flat metadata value at a time with a path such asuser.metadata.level. The final part must be a valid metadata key. Values are strings, numbers, booleans or null; missing keys read as null. Metadata is read-only in a condition, and the condition language keeps its existing limits on expression size, depth and work.

Drafts, versions and rollback

A flow has at most one draft, which you can change as often as you like, and numbered published versions, which never change. One published version is active; that's the one that runs.

  1. Under Flows for this point, enter a New flow name and select Create flow. A new flow starts as an empty draft.
  2. Add steps under New step, change them, and select Save draft.
  3. Before publishing, select Show the line-by-line diff to compare the draft with the active version, and try it with Run test.
  4. Select Publish draft. The draft becomes the next version and is active at once for every sign-in that uses the flow.

To go back, select Make active on an earlier version in Versions. Publishing checks every condition step against the flow's trigger, so a condition that reads a field the trigger doesn't have, or compares a field with a value it can never hold, is reported before it can run. Creating or publishing a flow changes nothing until a project or application uses it.

Owners and admins change flows. Viewers can read them, their versions and their traces. Every change is recorded in the project's audit log, without the step contents.

Test runs and traces

Run test runs the draft, or a chosen version, against a sample sign-in: an application, a sign-in method, a country and, optionally, an existing user. It stores nothing and writes no trace.Call your backend steps aren't called during a test run; they count as continuing.

Every real run of a flow writes a trace: the version, the trigger, the application, the outcome, the reason code and each step's outcome and duration. Recent executions in the editor lists the latest 50. Traces don't hold user IDs, email or IP addresses, or claim values, and are removed after 30 days.

Next