Risk blocks score signals. They do not make access decisions on their own. The pattern is always the same: a risk block gathers evidence, writes a recommendation to state, and then a Decision block downstream acts on it — routing to MFA, to a Deny terminal, or straight through to Issue Session.
The platform ships seven risk and security blocks covering every layer of the signal stack: network (IP reputation), automated traffic (bot detection), credential hygiene (brute force, breached passwords), account quality (email validation), and policy compliance (password policy). Add Risk Evaluate when you want all signals collapsed into a single score. Add individual signal blocks when you need finer control — for example, blocking bots at the top of a flow before any credential check runs.
The platform's defaults are tuned for general-purpose CIAM. You compose them; you decide what each signal triggers. A block that reports severity: high does nothing by itself until a Decision block acts on that value.
Picking a block
Section titled “Picking a block”- "Aggregate all active signals into one score and branch on it" — Risk Evaluate
- "Check this IP against threat feeds, VPN/Tor detection, and country denylist" — IP Reputation
- "Is this User-Agent or request rate pattern a known bot?" — Bot Detection
- "Has this identifier hammered us with failed logins?" — Brute Force Check
- "Has this password appeared in a public breach?" — Breached Password Check
- "Is this email address well-formed and not a throwaway domain?" — Email Validation
- "Does this password meet our complexity rules?" — Password Policy
Risk Evaluate
Section titled “Risk Evaluate”Block id: risk_evaluate · Available in: Login, Registration, Password Recovery, MFA Step-up
Run all registered risk signals — Brute Force, IP Reputation, HIBP, Bot Detection — and aggregate them into a single severity level and numeric score. The block writes a recommendation (allow, challenge, or deny) to state and sets the routing branch key so a downstream Decision block can act without writing its own expression. It also writes event.authentication.risk_score so the risk score is visible anywhere in the flow, including to Custom Actions running later.
This block is the fastest way to add adaptive MFA to an existing login flow. Drop it into your Post-Auth stage, wire a Decision block on step.<your-slug>.recommendation, and route challenge to an MFA block.
Reads from state
event.user.id— identifies whose signals to queryevent.request.ip— IP used for IP Reputation and Brute Forceevent.request.user_agent— User-Agent passed to bot detectionevent.request.device_fp— device fingerprint enriching the bot signal
Writes to state
step.<slug>.severity— aggregate risk level:none,low,medium,high, orcriticalstep.<slug>.score— numeric score 0–100 derived from all active signalsstep.<slug>.recommendation— engine recommendation:allow,challenge, ordenystep.<slug>.reasons— list of signal IDs that contributed, e.g.["brute_force", "vpn"]step.<slug>.signal_results— detailed per-signal results for audit and debuggingevent.authentication.risk_score— the same score written to the event namespace for downstream access
Use when — you want a single block to run all risk signals and produce one authoritative recommendation. Pair immediately with a Decision block branching on step.<slug>.recommendation.
Cautions — This block fails open: if individual signals are unavailable (for example, the HIBP service is unreachable), they contribute zero to the score rather than halting the flow. Check step.<slug>.signal_results in audit if you need to know which signals fired.
IP Reputation
Section titled “IP Reputation”Block id: ip_reputation · Available in: Login, Registration, Password Recovery, MFA Enrollment, MFA Step-up, and custom flows
Check the client IP against threat feeds, GeoIP country data, and ASN classification. You configure a per-classification policy — allow, challenge, or block — for each of VPN, Tor, datacenter, and bogon IPs. CIDR allow/blocklists provide unconditional overrides (useful for corporate office ranges). A country denylist lets you refuse traffic from specific regions outright.
The block sets a branch key that a downstream Decision block reads directly. You don't need to write your own expression; the engine routing key is already set.
Reads from state
event.request.ip— the client IP to evaluate (populated automatically at flow start)
Writes to state
step.<slug>.ip_reputation— overall reputation label:clean,suspicious, orblockedstep.<slug>.ip_classifications— list of ASN/behavioural labels that matched, e.g.["vpn", "datacenter"]step.<slug>.ip_threat_feed_hits— threat feed entries the IP matchedstep.<slug>.ip_threat_categories— threat categories (malware, phishing, spam, etc.)step.<slug>.ip_country— ISO country code, e.g.INstep.<slug>.ip_asn— autonomous system numberstep.<slug>.ip_asn_org— autonomous system namestep.<slug>.branch— routing key for a downstream Decision block
Use when — you want to challenge or block traffic from VPNs, Tor exit nodes, known malicious ASNs, or specific countries without writing custom logic.
Bot Detection
Section titled “Bot Detection”Block id: bot_detection · Available in: All flows
Check the incoming request for automated bot signals — headless browser User-Agents, unusual request-rate patterns, and known crawler signatures. The block is fail-open: if signal data is absent (for example, no User-Agent header) it writes detected: false and the flow continues.
Reads from state
event.request.user_agent— the request's User-Agent stringevent.request.ip— used for rate-burst pattern analysis
Writes to state
step.<slug>.detected—truewhen the request matches bot patternsstep.<slug>.severity— confidence level:none,low,medium, orhighstep.<slug>.reasons— signal IDs that triggered classification, e.g.["ua_headless", "rate_burst"]branch— routing key for a downstream Decision block
Use when — you want to short-circuit credential checks for clearly automated requests, or you want to log bot activity for threat analysis without blocking users.
Brute Force Check
Section titled “Brute Force Check”Block id: brute_force · Available in: All flows
Check how many consecutive failed login attempts have been recorded for this identifier within the current window. The block increments the counter on each failed attempt recorded upstream; the admin configures per-IP and per-identifier thresholds in the tenant's security settings. When a threshold is exceeded the block sets branch so a downstream Decision block can lock the account, require MFA, or prompt a CAPTCHA.
Reads from state
event.user.email(or the identifier supplied to the flow) — the account being checked
Writes to state
step.<slug>.severity— failed-attempt severity:none,low,medium,high, orcriticalstep.<slug>.count— number of consecutive failed attempts in the current windowstep.<slug>.reasons— which limit was exceeded, e.g.["per_ip_limit"]branch— routing key for a downstream Decision block
Use when — you want to gate on failed-attempt history before a password check runs, or you want to trigger account lockout / CAPTCHA after N failures.
Breached Password Check
Section titled “Breached Password Check”Block id: breached_password · Available in: All flows
Check whether the supplied password appears in the HaveIBeenPwned breach corpus. The platform uses k-anonymity — only the first five characters of the SHA-1 hash are sent to the HIBP API, so the plaintext password and full hash never leave the platform. The block is fail-open: a network error or API timeout writes breached: false and the flow continues.
Reads from state
event.credentials.password— the password candidate submitted by the user
Writes to state
step.<slug>.breached—truewhen the password appears in the HIBP corpusstep.<slug>.severity— severity based on exposure count:none,low,medium, orhigh
Use when — you want to reject known-bad passwords at registration or login, or you want to prompt users to change a compromised credential without hard-denying them.
Cautions — The block never stores or logs the plaintext password. Audit records mark the password field as redacted. Place this block early in a registration flow so a policy violation is caught before the identity is created.
Email Validation
Section titled “Email Validation”Block id: email_validation · Available in: All flows
Validate an email address format and check it against a disposable-domain blocklist. The check runs entirely in-process — no external calls, no latency. You can extend the built-in blocklist by adding domains to the block's blocklist config field. Set fail_on_invalid: true to have the block deny the flow automatically when the address fails; leave it false to branch on step.<slug>.valid manually via a Decision block.
For example, Cymmetri blocks free-tier signups from throwaway email services by adding a short domain list to this block's config and routing step.<slug>.valid == false to a Deny terminal with a friendly message.
Reads from state
event.user.email— the address to validate
Writes to state
step.<slug>.valid—truewhen the address passes format checks and is not on the disposable-domain blockliststep.<slug>.reason— why the address was rejected, e.g."disposable_domain"or"invalid_format"; empty when valid
Use when — you want to reject disposable or malformed addresses before creating an account or sending a verification email.
Password Policy
Section titled “Password Policy”Block id: password_policy · Available in: All flows
Enforce password complexity rules — minimum length, uppercase letter, lowercase letter, digit, and symbol — against a candidate password. The check is pure in-process with no external calls. Configure the rules you want in the block's settings; leave a rule disabled to not check for it. When the password fails, step.<slug>.reasons lists the specific rules that were not met so you can surface a precise message to the user.
Reads from state
event.credentials.password— the password candidate to evaluate
Writes to state
step.<slug>.passed—truewhen the password satisfies all configured complexity rulesstep.<slug>.reasons— rules the password failed, e.g.["min_length", "require_upper"]; empty when passed
Use when — you want to enforce a password policy at registration or when a user sets a new password during recovery.
Cautions — Place this block before any credential-set or identity-create block so a policy violation is caught before any write happens.
Two patterns
Section titled “Two patterns”Adaptive MFA on risk score
Section titled “Adaptive MFA on risk score”Drop Risk Evaluate into your Post-Auth stage. Add a Decision block immediately after it. In the Decision block, create two branches:
- When
step.risk-eval.recommendation == "challenge"— route to your MFA block - Default — route to Issue Session
Users who present clean signals go straight to a session. Users who present elevated signals are challenged. No hardcoded rules; the risk engine adapts as signals change.
Block known-bad credentials at signup
Section titled “Block known-bad credentials at signup”At the top of your Registration flow — before identity_create runs — add Breached Password Check followed by a Decision block:
- When
step.breach-check.breached == true— route to a Deny terminal with the message "That password has appeared in a known data breach. Please choose a different one." - Default — continue to Email Validation, then Identity Create
This pattern stops compromised passwords from ever being stored. It costs one in-process check and has no false-positive risk because you're branching on true/false, not a score.