Feature breakdown · built with CraftHQ
Customer sign-in with a one-time code (2FA)
Whole-team time at this build's pace
2 people incl. the product owner, requirements to merge
Estimate at this build's pace, not a measurement of this feature
External customers sign in with a password and a one-time code sent by email, and can recover access on their own.
How long a feature this size usually takes
Whole-team time — this build vs a typical team
19 FP · FP × 6.8 to 9.4 h (ISBSG 2023)
Estimate at this build's pace, not a measurement of this feature
Both figures are whole-team time over the whole lifecycle, so they compare like for like. Ours is this build's pace — about 0.8 hours per function point across the whole build (two people, including the product owner, with requirements and review time). The industry range is an ISBSG project-level figure applied to one feature, so it is indicative.
Workflow
How it moved through the workflow
Select a stage. The build shipped, so every stage is complete; a person approved the merge.
Request
External customers need two-step sign-in with a way to recover access on their own, so staff are not resetting passwords by hand.
Questions
One decision was recorded before building: how the one-time code should be entered.
Approved
The product owner signs off the breakdown by moving the story into the approved state, and the build runs against that reference.
Definition of ready
Building
CraftHQ's build agents write the code and the tests together, run as a loop; a failing check halts the work until it passes.
Checks
Three checks run on every pull request: one on the product owner's machine as CraftHQ builds, one on the engineer's machine before review, and a code-quality scan in CI. Merging is gated by the CI check passing and a person's approval, enforced by branch protection.
Review
An engineer reviews the pull request and merges it.
Merged
The pull request is merged to main after a person approves it. Nothing merges without that approval.
Definition of done
Breakdown
The feature breakdown
Business first, then the engineering view. Tab through it.
Overview
The gap it closes
External customers need a second factor and a way to recover access on their own, without staff resetting passwords by hand.
Who uses it
Users from a customer organisation signing in to their own workspace. Staff sign in separately, which is out of scope here.
User story
As a customer user, I want to sign in with a password and a one-time code so that a leaked or reused password alone does not let someone in.
Examples
Example 1
A customer enters their email and password, then the current one-time code from their inbox, and lands in their organisation's workspace.
Example 2
Someone tries codes at random. After three wrong codes the code is locked for fifteen minutes and must be re-issued, and asking for a fresh code is held to one every two minutes and three in fifteen, so a six-digit code cannot be ground down by guessing.
Dependencies & risks
Depends on
A way to deliver invites, reset links and one-time codes by email, and a server-side session store that can be revoked.
Risk & mitigation
A shared or guessed password alone would be weak. The one-time code adds a second factor; codes expire, are single-use and are rate-limited; and sessions can be revoked on the server.
How it works
Simplified diagrams of the parts involved, the steps of one request, and where it runs.
Architecture
- 1email and password
- 2second factor
- 3resolve the account and verify the password
- 4issue a code by email
- 5start session on a valid code
- 6record outcome
Sequence
- 1email and password
- 2resolve the account and verify the password
- 3send a one-time code by email
- 4enter the code
- 5check the code (unused, unexpired, within attempt limit)
- 6start a session
- 7signed in to the organisation
Infrastructure
- 1sign-in request (authenticate + tenant check at the edge)
- 2read account, write session
- 3send code or reset link
Acceptance criteria
Each scenario is written as a Gherkin spec. The Engineering tab shows the test that covers each one, or notes where it was verified by review.
@sign-in @securityFeature: Customer sign-in with a one-time codeAs a customer userI want to sign in with a password and a one-time code sent by emailSo that a leaked or reused password alone does not let someone inExternal customers sign in with a password and a one-time code by email, and can recover access on their own.Background:Given a customer belongs to one organisationAnd the customer has been invited to that organisationRule: Sign-in needs a password and a one-time codeScenario: A customer signs in with both factorsGiven a customer enters their email and correct passwordWhen they enter the current one-time code from their inboxThen they are signed in to their organisationScenario: A wrong one-time code is refusedGiven a customer enters their email and correct passwordWhen they enter an incorrect one-time codeThen they are not signed inScenario: Too many wrong codes lock sign-inGiven a customer has entered a correct passwordWhen they submit the wrong one-time code more than the allowed number of timesThen sign-in is locked for fifteen minutesScenario: A used code cannot be replayedGiven a customer has already signed in with a one-time codeWhen the same code is submitted againThen it is refusedScenario: A resend within the cooldown is refusedGiven a customer is at the one-time-code stepWhen they ask to resend the code within the cooldownThen the resend is refused until the cooldown passesScenario: A fresh code replaces the old oneGiven a customer asks to resend the code after the cooldownWhen a new code is issuedThen the earlier code no longer worksRule: A customer can recover access without helpScenario: Forgotten password reset does not reveal whether an email existsGiven a person asks to reset a password for any email addressWhen they submit the requestThen the response is the same whether or not the email is registeredScenario: A reset link is single-use and expiringGiven a customer has a password-reset linkWhen they complete the resetThen they can set a new password and sign inAnd the link cannot be used againAnd their existing sessions are revokedRule: A session can be ended on the serverScenario: A session is ended centrallyGiven a customer is signed inWhen the server revokes the session, for example on a password resetThen the customer's next action requires signing in again@edge-casesScenario Outline: One-time-code outcomesGiven a customer has entered a correct passwordWhen they submit a "<code>" one-time codeThen the result is "<result>"Examples:| code | result || current | signed in || expired | refused || already used | refused || incorrect | refused |
Questions
Questions raised in planning, each with the decision taken.
How should the one-time code be entered?
A clear code-entry step reduces mistakes and handles paste, backspace and arrow keys the way people expect.
How we sized it
A function point measures what the software does — its inputs, outputs, lookups and the data it stores — regardless of language or how many lines it took. Each counted item is weighted by complexity: a user input counts 3–6 points, an output or report 4–7, a lookup 3–6, data the feature owns 7–10, and data read from elsewhere 5–7.
- Sign in with email and passwordUser input4
- Verify the one-time codeUser input4
- Resend a one-time codeUser input3
- Request a password resetUser input4
- Complete a password resetUser input4
- Total19
Not counted here
- Codes, sessions and reset-link plumbing: technical, not a user function
- The account store: counted once, in its owning feature
Components touched
Sign-in step
Takes the email and password for a customer within one organisation; passwords are stored only as salted hashes, never in clear text.
One-time-code gate
After the account is resolved and the password verified, it issues a six-digit code by email, checks it, and lets the customer request a fresh one. Codes are stored only as a hash, expire after five minutes, are single-use, are invalidated when a new one is issued, and lock after three wrong attempts for fifteen minutes; resend is limited to a 120-second cooldown and three in fifteen minutes.
Forgot and reset
A self-service path. The reset link is single-use and expires; the request step does not reveal whether an email is registered; a completed reset revokes existing sessions.
Session store
Server-side sessions with a resolver and a session revocation, so a session can be ended centrally and all sessions dropped on a password reset.
Invite and admin reset
An administrator can invite a customer and reset their access.
Data touched
Customer accountsread
Resolved per organisation at sign-in, keyed by the login email; passwords held as salted hashes.
One-time codeswrite
A six-digit, single-use code per sign-in attempt, stored as a hash, with a five-minute expiry, an attempt counter and a resend cooldown.
Sessionswrite
Created on a successful two-step sign-in, revocable on the server, and dropped on a password reset.
Delivery serviceexternal
Sends invites, reset links and one-time codes by email.
Security decisions
- Sign-in requires a password and a one-time code; passwords are stored as salted hashes only.
- The six-digit code is delivered by email, stored only as a hash, expires after five minutes, is single-use, is invalidated when a new code is issued, and locks after three wrong attempts for fifteen minutes; resend is held to a 120-second cooldown and three in fifteen minutes.
- Forgot-password does not reveal whether an email is registered; the reset link is single-use and expires; a completed reset revokes all existing sessions.
- Sessions are server-side and can be revoked, so access can be ended centrally.
Subtasks
- Per-organisation email and password sign-in with hashed passwords.
- A one-time-code gate with expiry, single-use, attempt lockout and a resend cooldown.
- A forgot-and-reset path with a single-use expiring link and session revocation.
- Server-side sessions with session revocation.
How each scenario is verified
- A customer signs in with both factorsAn integration test drives the sign-in flow from password to code to a started session.
- A forgotten password resetA test over the reset path checks the link is single-use and that a reset revokes sessions.
- A wrong or expired one-time code is refusedChecked within the sign-in flow integration test.
- A session is revokedVerified by review. Revocation uses the session store's version mechanism.
Risk tier
Core A person approves every merge. This is a Core-tier change, so it also carries the fullest set of automated tests; a regression here affects the whole product, not one page.
Start here
Want this on your backlog?
Get a free baseline report on one repository, or scope a 3-month pilot measured against what your team’s work costs today.