Skip to content

Block reference — Authentication

The Authentication family is the heart of any flow. These eleven blocks verify what users know (passwords), what they have (OTP codes, passkeys), and whether their second factor is enrolled and satisfied. The platform owns all secret handling end-to-end — your flow composes verifications, but it never touches a plaintext credential or a raw cryptographic key. That boundary is non-negotiable and enforced at every call site.

Use this page alongside the Flows concepts page and the {} picker in the editor. The picker shows you what's referenceable at each step; this page tells you what each block promises to write there.

You want to...Reach for
Verify a returning user's passwordVerify Password
Enroll a new credential at signup or during an admin-create pathSet Credential
Accept a new password at the end of a password resetUpdate Password
Dispatch a one-time code to email or SMSSend Channel OTP
Verify the code the user typed backVerify Channel OTP
Authenticate a passkey or hardware keyWebAuthn Begin + WebAuthn Finish
Verify a second factor (TOTP, OTP, passkey, or SMS)Multi-Factor Authentication
Look up whether the user has MFA enrolled, without acting on itCheck MFA Status
Enforce a specific factor at this exact point in the flowRequire MFA
Gate entry until the user has enrolled a factorRequire MFA Enrollment

Block id: password_check  ·  Available in: Login, Registration, Password Reset, MFA Enrollment, MFA Step-up

Verify the user's password credential. Lifts the session to AAL1 on success. This is the standard first factor for any password-based login flow — place it after Identity Lookup, then branch on step.<slug>.verified with a Decision block to route failures to a retry screen.

Reads from state

  • identifier — the user's email or username, resolved upstream by Identity Lookup
  • password — submitted by the user via the SDK login form

Writes to state

  • step.<slug>.verifiedtrue when the supplied password matched the stored credential
  • step.<slug>.factor_id — internal identifier for the credential that matched

Use when — the user is logging in or re-authenticating with a password and you want AAL1 lifted on success.

Cautions — Password Check is fail-closed: if the credential store is unreachable, the block returns a deny rather than passing through. Pair with a rate-limiting block upstream (Brute Force Check) to limit repeated attempts.


Block id: credential_set  ·  Available in: Login, Registration, Password Reset, MFA Enrollment, MFA Step-up

Enrol a new credential for the user — password, TOTP, channel OTP, or passkey. This is the registration tail-end block, the user-facing MFA-enrolment block in the Security tab, and the mechanism behind an admin's "set initial password" path. The block is pause-capable: for TOTP it emits a QR-code pause, for WebAuthn a ceremony pause, for SMS / email OTP a "we sent a code" pause. The flow engine surfaces each pause to the SDK, the user fills the input, and the block resumes through Aegis's selfservice ceremony to finalise the credential.

Reads from state

  • user_id — the user being enrolled, set by Identity Create upstream
  • password — for password-type enrolment, the new password submitted by the user
  • phone — for SMS-OTP enrolment, the verified phone number
  • pipeline.run_id — opaque correlator the block uses to look up the Aegis ceremony triplet (flow_id, csrf_token, csrf_cookie) it stashed at begin-time. Minted at /start; never goes through the stage token's state.

Writes to state

  • step.<slug>.credential_id — identifier of the newly enrolled credential, present when the factor type returns one
  • step.<slug>.pause — typed pause envelope (kind: "credential_set", factor: <type>, challenge: …) the FE renders the next screen from

Configuration knobs

KnobPurpose
factor_type_idThe factor to enrol — password, totp, sms_otp, email_otp, webauthn_platform, webauthn_security_key, backup_code, or the special string "any"
available_factor_typesOptional allow-list to restrict the picker when factor_type_id is "any"
optionalWhen true, the FE renders a "Skip for now" button on the pause screen; resuming with { action: "skip" } advances past the block without enrolment
skip_if_enrolledWhen true, the block becomes a no-op when the user already has a credential of this factor type (defensive default for registration-time enrolment that may run for re-imported users)

Factor-picker mode (factor_type_id: "any"). Set Credential emits an additional picker pause before the per-factor pause. The user sees a card grid of factors they can enrol — the intersection of available_factor_types, the tenant-allowed factors, and what the user has not already got. The user picks one and the block proceeds with that factor; their per-profile preferred factor is pre-highlighted.

Use when — the user is completing registration and you need to persist their chosen password, the user is adding a method from the Security tab, or you want to gate access until a missing factor is enrolled.

Cautions — The audit log redacts the password field automatically. WebAuthn enrolment binds the credential to the origin the ceremony ran on; an enrolment at https://staging.acme.com cannot be verified from https://acme.com. For TOTP, the abandonment TTL is 15 minutes — if the user closes the page mid-enrolment, the secret is discarded and the next attempt mints a fresh one.


Block id: password_update  ·  Available in: Password Reset only

Replace the user's password. Self-service flows verify the old password upstream (using Verify Password) before this block runs; admin-reset flows skip that check. Update Password is intentionally scoped to the Password Reset flow — placing it in any other flow type is a validation error.

Reads from state

  • user_id — the user whose password is being replaced
  • password — the new password submitted by the user

Writes to state

  • (no declared outputs)

Use when — you are building or customising the Password Reset flow and need the credential replacement step.

Cautions — The audit log redacts the password field. For self-service resets, always place Verify Password first so the user proves they know the current password before replacing it. Admin-initiated resets may omit the upstream check.


Block id: channel_otp_send  ·  Available in: Login, Registration, Password Reset, MFA Enrollment, MFA Step-up

Dispatch an OTP code to the user's verified channel — SMS or email. The platform resolves the channel binding through Aegis; this block does not see the recipient address directly. Pair it immediately with Verify Channel OTP so the user can submit the code on the same screen.

Reads from state

  • user_id — the user to dispatch the code to, resolved upstream

Writes to state

  • step.<slug>.otp_id — internal handle for the dispatched code
  • step.<slug>.masked_recipient — partially redacted address the OTP was delivered to (e.g. +1******1234), safe to display in UI
  • step.<slug>.expires_at — ISO 8601 timestamp after which the code is no longer valid

Use when — building a passwordless login flow, adding SMS step-up, or sending a one-time code during account recovery.

Cautions — This block is fail-open: if the delivery channel is unreachable, the block passes rather than halting the flow. Monitor delivery failures through the audit log. On admin-create paths the block is bypassed by default (the OTP send toggle is on by default for skip).


Block id: channel_otp_verify  ·  Available in: Login, Registration, Password Reset, MFA Enrollment, MFA Step-up

Verify an SMS or Email OTP code submitted by the user. Pairs with the Send Channel OTP block (which dispatched the code) and appends the factor to event.authentication.methods on success. The block is fail-closed — an unreachable credential store returns a deny rather than a pass.

Reads from state

  • user_id — the user submitting the code
  • otp_code — the code typed by the user, submitted via the SDK

Writes to state

  • step.<slug>.credential_id — identifier of the verified OTP credential, present when the factor type issues one
  • event.authentication.methods — the array of verified factor types, updated to include this channel factor

Use when — you need to confirm that the user received and read the code sent by Send Channel OTP.


WebAuthn authentication is a two-block operation: WebAuthn Begin issues a challenge to the browser, and WebAuthn Finish verifies the response. They must always appear together in the same flow, in that order. Splitting them across stages or omitting either half is a flow authoring error.

Block id: webauthn_begin  ·  Available in: Login, Registration, Password Reset, MFA Enrollment, MFA Step-up

Start a WebAuthn authentication ceremony. The platform returns a PublicKeyCredentialRequestOptions blob the SDK passes to navigator.credentials.get(). The resulting assertion is round-tripped back to the platform and consumed by WebAuthn Finish.

Reads from state

  • user_id — the user initiating the passkey ceremony

Writes to state

  • step.<slug>.session_id — opaque handle linking this ceremony to the finish step; the SDK round-trips it back. Transient: not persisted across the ceremony boundary.
  • step.<slug>.options — the W3C PublicKeyCredentialRequestOptions blob passed to the browser. Transient.

Use when — you want to support passkeys (Touch ID, Face ID, YubiKey, Windows Hello) as a login factor or second factor.

Cautions — Both outputs are transient. They are delivered to the SDK in the /submit response and must not be read by later blocks. Configure user_verification in the block's config panel (required for step-up; preferred for login).


Block id: webauthn_finish  ·  Available in: Login, Registration, Password Reset, MFA Enrollment, MFA Step-up

Complete a WebAuthn authentication ceremony. The platform verifies the AssertionResponse against the challenge issued by WebAuthn Begin and updates LastUsedAt on the credential. This block can deny if the assertion is invalid or the credential is not registered for this user.

Reads from state

  • user_id — the user whose passkey is being verified
  • webauthn_session — the session handle from step.<begin-slug>.session_id, round-tripped by the SDK
  • webauthn_assertion — the browser's AuthenticatorAssertionResponse, submitted via the SDK

Writes to state

  • step.<slug>.credential_id — identifier of the passkey that was verified during the WebAuthn ceremony
  • event.authentication.methods — updated to include the WebAuthn factor

Use when — immediately after WebAuthn Begin, as the second half of every passkey ceremony.


Block id: mfa  ·  Available in: Login, Registration, Password Reset, MFA Enrollment, MFA Step-up

Verify a second authentication factor — TOTP, SMS-OTP, Email-OTP, or WebAuthn. The specific factor type is configured per-block; the AAL contribution to the session is resolved at runtime based on which factor the user passes. For step-up flows, place MFA after a Decision block that reads Check MFA Status output to confirm enrollment before challenging.

Reads from state

  • user_id — the user being challenged
  • payload — the factor response submitted by the user (TOTP code, OTP code, or WebAuthn assertion depending on configured factor type)

Writes to state

  • step.<slug>.verifiedtrue when the user successfully passed the configured MFA factor
  • step.<slug>.factor_id — identifier of the factor credential that was verified
  • step.<slug>.session_aal — the AAL level the session was lifted to after this verification
  • step.<slug>.pause — typed pause envelope. kind: "mfa.picker" on the picker step (when configured for "any"); kind: "mfa.verify" on the per-factor verify step

Picker mode (factor_type_id: "any"). Like Set Credential, the MFA block accepts "any" to let the user pick. The picker emits an mfa.picker pause listing the user's enrolled factors (minus the one already used in this flow run, if any). Once picked, the block emits the per-factor verify pause and proceeds. Restrict the picker contents with available_factor_types.

Use when — you want a single, configurable MFA challenge block in a login or step-up flow. For explicit per-factor control, prefer Require MFA.


Block id: check_mfa_status  ·  Available in: Login, Registration, Password Reset, MFA Enrollment, MFA Step-up

Read the user's MFA enrollment state via the platform. Writes mfa_enrolled and mfa_methods to state. This block is metadata-only — it never denies and never challenges the user. Use its outputs in a Decision or Condition block to route enrolled users to the MFA challenge and unenrolled users to enrollment.

Reads from state

  • user_id — the user whose enrollment state is being read

Writes to state

  • step.<slug>.mfa_enrolledtrue when the user has at least one active MFA factor of the required type
  • step.<slug>.mfa_methods — array of enrolled factor type strings
  • step.<slug>.mfa_preferred_method — the user's preferred factor type, if set

Use when — you need to branch a flow based on whether the user has MFA enrolled, without immediately challenging them. Typical pattern: Check MFA Status → Decision (read enrolled) → MFA challenge or enrollment redirect.


Block id: require_mfa  ·  Available in: Login, Registration, Password Reset, MFA Enrollment, MFA Step-up

An explicit MFA gate that delegates to the MFA evaluator with the configured factor_type_id. Unlike the MFA block, Require MFA is self-contained: it both checks and challenges in one step, without needing an upstream status check. It is fail-closed and can-deny — a failed challenge halts the flow.

Reads from state

  • user_id — the user being challenged
  • payload — the factor response submitted by the user

Writes to state

  • step.<slug>.verifiedtrue when the user passed the required MFA factor at this gate
  • step.<slug>.factor_id — identifier of the verified factor credential
  • step.<slug>.session_aal — the AAL level the session was lifted to

Use when — you want a single block that enforces a specific factor at a specific point in the flow, without a separate enrollment-check step. Cymmetri uses this pattern at the end of their Login flow for all users flagged as high-value accounts.


Block id: require_mfa_enroll  ·  Available in: Login, Registration, MFA Enrollment, MFA Step-up

Force a pending MFA enrolment when the user has no enrolled factor of the required type. Require MFA Enrolment is an orchestrator: it delegates to Set Credential under the hood, so it inherits the picker pause, the per-factor enrolment pause, and the optional / allow_skip behaviour without duplicating any of it. If you find yourself wiring "check status → if not enrolled → enrol" by hand with three blocks, replace the chain with this single block.

Reads from state

  • user_id — the user whose enrolment is being checked
  • pipeline.run_id — the ledger correlator threaded into the delegated Set Credential ceremony

Writes to state

  • step.<slug>.mfa_enroll_requiredtrue when the user reached the block without a usable factor
  • step.<slug>.mfa_enrolledtrue after the delegated enrolment succeeds (or if the user was already enrolled)
  • step.<slug>.credential_id — when an enrolment ran, the id of the newly enrolled credential
  • step.<slug>.pause — pause envelopes pass through unchanged from the delegated Set Credential (mfa.picker, then credential_set)

Configuration knobs

KnobPurpose
factor_type_idWhich factor to demand. Set "any" to surface the picker.
available_factor_typesAllow-list when factor_type_id is "any"
allow_skipSurfaces a "Skip for now" button on the enrolment screen; resume with { action: "skip" } to bypass without enrolling. Use this for "nudge, don't force" enrolment campaigns.

Use when — you want one block that turns "user has no MFA" into "user has MFA" without composing three blocks by hand. Place it after Password Check in Login flows; the user enrols once, then sails through every subsequent login.


These patterns appear in most tenants. The block IDs are illustrative — your flow editor assigns slugs per instance.

Password login with step-up MFA

Identity Lookup
-> Verify Password
-> Check MFA Status
-> Decision (step.check-mfa-status.mfa_enrolled == true)
yes -> Multi-Factor Authentication
no -> Issue Session (AAL1 only)
-> Issue Session (AAL2)

Place the Decision block to read step.check-mfa-status.mfa_enrolled. Users without MFA enrolled proceed to a session at AAL1; enrolled users are challenged and lift to AAL2.

OTP-based passwordless login

Identity Lookup
-> Send Channel OTP
-> Verify Channel OTP
-> Issue Session

No password in the path. The user proves access to their registered email or phone. If you want to enforce that the OTP channel is set up before this flow runs, gate entry with Check MFA Status upstream.

Adding a passkey to an existing user

WebAuthn Begin
-> WebAuthn Finish
-> Set Credential

WebAuthn Begin issues the registration challenge. WebAuthn Finish verifies the authenticator's attestation response. Set Credential enrolls the verified passkey against the user's identity. This sequence belongs in an MFA Enrollment flow, not a Login flow.