Security failures often appear at boundaries: one flow sets a value that another flow trusts, one role changes an assurance requirement, or an integration failure triggers a workaround that bypasses authorization. Unit tests are necessary, but they cannot fully express those relationships.
Model the product as connected state machines
List sensitive flows—signup, authentication, credential enrolment, recovery, role selection, provisioning and access approval—and identify shared states and records. Draw the dependencies between them. This exposes places where a change in one area may influence a supposedly separate path.
WebFrolic’s security paper highlights fragmented identity flows and chained attacks. It describes server-side extended state and transaction-based flows as ways to preserve history and validate transitions. Tests should exercise those properties across the whole request, not only one handler at a time.
Add adversarial combinations to the test plan
Think like an attacker who can use ordinary features in an unusual order. Can a user enrol a new authenticator and then satisfy a strong role requirement? Can recovery leave old API keys working? Can a tenant administrator change an identifier to reach another tenant? Can a partial provisioning failure leave access behind?
State the expected policy outcome and verify the audit trail too. A denial without an intelligible record is hard to investigate; a success without evidence is hard to validate. Security assertions belong in the functional result and operation history.
Test failure and retry paths
Integrations time out, clients retry, requests are duplicated and workers process events out of order. Sensitive operations must be safe under those conditions. Check idempotency, rollback or compensation, partial completion and what happens when a dependency is unavailable.
Fallbacks deserve extra attention. Emergency support, alternative authenticators and integration recovery may be necessary, but can create hidden routes around the main policy. Test every fallback as a first-class flow with explicit preconditions and audit events.
Make the test suite evolve with policy
When a feature changes shared state, role assignment, authenticator configuration or recovery, expand regression coverage to adjacent flows. Maintain a set of invariants—for example, “a new factor cannot authorise its own enrolment”—and test them wherever relevant.
A complete test matrix can grow quickly, so prioritise privilege boundaries, cross-tenant operations, credential changes, recovery and high-impact configuration. Security testing should be a release discipline rather than a one-time assessment.
What to ask your own team
Map interactions among flows, roles, credentials and integrations?
Maintain explicit security invariants and test relevant paths?
Exercise retries, partial failures, fallbacks and out-of-order operations?
Review final decisions and correlated audit evidence?
