Skip to content
All essays

Mobile architecture

Security requirements that cannot be tested are not finished

A security requirement becomes useful when a team can observe the behaviour, test the boundary and know who owns failure.

By Mike Salari. Staff mobile engineer · 500+ technical interviews from the hiring side.

3 min read

Listen to this articleComplete narrated version of this article

"Store tokens securely" sounds responsible. It is not yet a requirement a team can finish.

Which tokens? Against which attacker? What behaviour is forbidden? What must happen after logout, device restore, biometric change or refresh failure? Who can prove the release still behaves that way?

Until those questions have observable answers, security remains a direction rather than a product boundary.

Write the attacker and the protected behaviour

Do not begin with the library. Begin with what the attacker can do and what the product must prevent or contain.

An acceptance criterion might say that a copied application database cannot reveal an active refresh token, or that a logged-out device cannot reuse cached authenticated content. Both statements can be tested. "Use secure storage" cannot.

Failure behaviour belongs in the requirement

Security mechanisms fail in production. Attestation is unavailable. A key is invalidated. The network cannot reach the refresh service. A migration meets data written by an older version.

The requirement must say whether the product blocks, degrades, retries, clears state or asks the user to authenticate again. If the failure mode is unspecified, the implementation will invent it under pressure.

Sign-out and the late response

Take logout. Local sign-out and server-session invalidation are two obligations, and a cleared screen proves neither.

Once local sign-out completes, a refresh started under account A must not write credentials or screen state into any later session. Whether the server session was invalidated is reported separately.

This is the part of a token manager that enforces it. Logout bumps a generation and drops the in-flight refresh. A late response can commit only if its generation, its flight and its account still match.

Swift
public func logout() throws {
    generation += 1
    flight?.task.cancel(); flight = nil
    try store.replace(nil)
    credentials = nil
}

private func commit(_ next: Credentials, account: String, epoch: Int, id: UUID) throws -> Credentials {
    guard generation == epoch, flight?.id == id, credentials?.accountID == account else {
        throw SessionError.invalidated
    }
    guard next.accountID == account else { throw SessionError.wrongAccount }
    try store.replace(next)
    credentials = next
    flight = nil
    return next
}

The order is deliberate. The generation bump comes first, so a late response is already rejected while the store write runs. Memory clears only after the store write succeeds: if the write throws, logout reports the failure and the caller can retry instead of showing a signed-out screen over credentials still on disk.

The test that matters uses a refresh client that ignores cancellation, because cancellation alone would let its late response through. A test like this checks that a late response after logout cannot restore credentials, and a negative control that removes the guard must make the logout test fail. Keychain behaviour on a physical device, backup and reinstall, and server-side invalidation stay on the release gate until someone runs them.

Collect evidence at the boundary

A passing unit test may prove that a wrapper called an API. It does not prove that sensitive data stayed out of logs, backups, screenshots or analytics payloads.

Choose evidence that reaches the boundary you claim to protect. That may include an integration test, a device inspection, a proxy capture, a release configuration check or a documented manual verification.

Assign an owner and an expiry condition

Threats, platform behaviour and dependencies change. A control without an owner becomes a historical belief.

Record who reviews the requirement, which event forces a new review and which evidence the release must retain. The result is a behaviour the product team can operate.

Security Is a Feature is the book for the requirements this essay says have to be testable.

Related resources

Free resources that put this essay to work: short enough to use this week, and each one works on its own, with or without a book.

Related books

These books take the subject of this essay further, written from the hiring side from 500+ technical interviews. Each book page shows what is inside, who it is for, and a free sample where there is one.

Share this essay