Feature breakdown · built with CraftHQ
Single sign-on with Microsoft
Hands-on build time
~1.7 to 2.7 h
Measured — hands-on build time, a lower bound from commit activity
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
Staff sign in with the Microsoft work account they already use, so there is no second password to manage.
How long a feature this size usually takes
Whole-team time — this build vs a typical team
10 FP · FP × 6.8 to 9.4 h (ISBSG 2023)
Estimate at this build's pace, not a measurement of this feature
Hands-on build time: ~1.7 to 2.7 h, measuredhands-on build time, a lower bound from commit activity
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
Staff should not carry a separate password for the portal; they should sign in with the Microsoft work account they already use. Only listed, active staff get in, and an administrator can end access by deactivating a user in the staff list.
Questions
Three decisions were recorded before building, each with a recommended answer: password fallback or Microsoft only; trust the provider for access or only for identity; and whether the app locks accounts itself.
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
This feature was built before the pull-request flow; it was built, reviewed and merged directly to main, with a person approving the merge. Later features run through the three checks described across the library.
Review
An engineer reviewed the change and merged it; one fix was made before merge.
Merged
Merged to main after a person approved 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
Staff juggle a separate portal password, and a person who leaves can linger until someone remembers to switch them off.
Who uses it
Every staff role (administrators, staff and reviewers) uses the same sign-in button; the role decides the dashboard they land on. Customers are not staff and use a separate sign-in, out of scope here.
User story
As a staff member, I want to sign in with my Microsoft work account so that I never manage a separate password, and only active, listed staff can get in.
Examples
Example 1
An active staff member clicks Sign in with Microsoft, completes sign-in, and lands on the dashboard for their role. The sign-in is recorded.
Example 2
An administrator deactivates a signed-in staff member in the staff list. The active flag is re-checked on every request, so their next action is refused and the session ends, without waiting for the idle timer.
Dependencies & risks
Depends on
The organisation's Microsoft directory, read for identity only, and the staff list, which an administrator maintains. Each staff member is matched to their directory account - primarily on the organisational email claim, and pinned to the directory's immutable object id on first sign-in; a record pinned to a different id is refused.
Risk & mitigation
A stale staff list could let a departed person in. Reduced by re-checking the active flag in the staff list on every request and ending the session straight away when a permission changes.
How it works
Simplified diagrams of the parts involved, the steps of one request, and where it runs.
Architecture
- 1opens
- 2redirect to sign in
- 3exchange the code for a token
- 4identity token (response)
- 5look up role by directory id
- 6start session
- 7append the outcome
Sequence
- 1start sign-in
- 2mint anti-forgery value, nonce and PKCE in an encrypted cookie
- 3redirect to sign in
- 4the person signs in
- 5return with a one-time code
- 6exchange the code for a token
- 7identity token
- 8validate state, nonce, audience, signature and expiry
- 9look up by directory id, active?
- 10role and active flag
- 11append the sign-in outcome to the audit log
- 12start a session, open the dashboard
Infrastructure
- 1sign-in request (authenticate + tenant check at the edge)
- 2authorization-code flow
- 3read staff list, append audit
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: Single sign-on with MicrosoftAs a staff memberI want to sign in with my Microsoft work accountSo that I never manage a separate password, and only active, listed staff can get inStaff do not carry a second portal password; sign-in reuses the Microsoft work account they already have.Background:Given the approved company email domains are configuredAnd the staff list is maintained by an administratorAnd each staff member is matched to their directory account, pinned to its immutable object id on first sign-inRule: Microsoft proves who the person is; the staff list decides accessScenario: A listed, active staff member signs inGiven a staff member has an active Microsoft work account on an approved domainAnd that person is on the staff listWhen they sign in with MicrosoftThen they land on the dashboard for their roleAnd the sign-in is recordedScenario: The role decides the landing pageGiven an administrator has set a staff member's role in the staff listWhen that staff member signs inThen they land on the dashboard for that roleRule: Only active, listed accounts may sign inScenario: An account not on the staff list is refusedGiven a person signs in with Microsoft but is not on the staff listWhen the application checks them against the staff listThen they are refused and returned to sign-inAnd no account is createdScenario: A sign-in from a domain that is not approved is refusedGiven a person signs in with Microsoft from a domain that is not on the approved listWhen the application checks the domainThen they are refused before any account is resolvedScenario: An account disabled in the directory cannot sign in againGiven a staff member has been disabled in the Microsoft directoryWhen they try to sign inThen Microsoft refuses the sign-inAnd on return the application shows the returned errorRule: Access ends when an administrator deactivates the user in the staff listScenario: Deactivation ends access on the next requestGiven a staff member is signed inAnd an administrator deactivates them in the staff listWhen the staff member takes their next actionThen the action is refused because the active flag is re-checked on every requestAnd their session ends without waiting for the idle timerScenario: A role change ends the open sessionGiven a staff member is signed inWhen an administrator changes their roleThen their session ends on the next request, as for deactivationRule: A session left idle is signed outScenario: Idle timeoutGiven a staff member has been idle for the configured timeoutWhen they take their next actionThen they are signed outAnd returned to sign-in with a "session expired" noticeRule: The sign-in callback is validated before any session starts@edge-casesScenario Outline: A tampered or invalid callback is refusedGiven a person returns from Microsoft to the sign-in callbackAnd the callback is "<condition>"When the application validates the callbackThen sign-in is refused and no session is startedExamples:| condition || carrying a state or nonce that does not match || reusing an authorization code already spent || carrying a token issued for another tenant || cancelled by the person at Microsoft |Scenario: An off-site return path is sanitised, and sign-in proceedsGiven a person returns from Microsoft to the sign-in callbackAnd the return path points off-siteWhen the application validates the callbackThen the return path is sanitised to an internal application pathAnd sign-in proceeds and the person lands on an internal page, with no open redirect
Questions
Questions raised in planning, each with the decision taken.
Should staff keep an application password as a fallback, or sign in only with Microsoft?
A fallback password re-opens the weak door that single sign-on is meant to close.
When Microsoft vouches for someone, do we trust it for what they may do, or only for who they are?
Keeping the access decision with the company, not the identity provider, keeps that decision and its record in one place.
Should the app count failed sign-ins and lock accounts itself?
With no password on our side there is nothing to count, and an app-side counter would re-introduce the surface single sign-on removed.
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 a Microsoft work accountUser input6
- Record the directory identity on first sign-inUser input4
- Total10
Not counted here
- Session and token plumbing: technical, not a user function
Components touched
Configuration
The sign-in settings for the directory provider.
Redirect routes
Two browser-redirect routes, start and callback.
Provider
The identity provider's sign-in library behind a thin, swappable interface, so tests can drive it with a stand-in.
Transaction cookie
A short-lived, signed-and-encrypted, http-only cookie carrying the anti-forgery value, nonce and PKCE between the two redirects. No database row, no migration.
Sign-in handler
Exchanges the authorization code for a token, validates every claim, resolves the role from the staff list - matching primarily on the organisational email claim and pinned to the immutable object id - refuses anyone not listed, and starts a fresh session.
Sign-in screen
Renders the single-sign-on view, makes the button a real navigation, and shows a banner on error.
Data touched
Staff listread/write
Matched to the directory account - primarily on the organisational email claim, and pinned to the directory's immutable object id on first sign-in; a record pinned to a different id is refused - to resolve the role and the active flag. The directory id is recorded on first sign-in; roles and the active flag are maintained by an administrator.
Sessionswrite
A fresh session is started on success, and torn down on sign-out, idle expiry, or a permission change.
Audit logappend
Sign-in outcomes for staff accounts, success or failure: what happened, never the secret.
Identity providerexternal
Read-only, for identity. None of its credentials are stored.
Security decisions
- Refuse by default: an unlisted or inactive identity is refused outright, never created automatically.
- The callback token is validated in the app: signature against the published keys, issuer, audience, tenant, nonce and expiry; the state value is checked on the callback.
- An anti-forgery value, a nonce and PKCE defend against cross-site request forgery, replay and code interception; a reused authorization code is refused.
- An encrypted, http-only transaction cookie with a short lifetime; a fresh session id on sign-in.
- The return path is sanitised to an internal page only, so there is no open redirect.
Subtasks
- Sign-in settings for the directory provider.
- Start and callback redirect routes.
- Identity-provider library behind a swappable interface.
- Encrypted http-only transaction cookie for the anti-forgery value, nonce and PKCE.
- Sign-in handler: exchange the code, validate every claim, resolve the role by the email claim pinned to the immutable object id, refuse unlisted, start a session.
- Sign-in screen: the sign-on view and the error banner.
How each scenario is verified
- A listed, active staff member signs inAn integration test drives the real sign-in library against a stand-in provider, from the start redirect to a started session.
- The role decides the landing pageThe sign-in flow test checks the role and landing page are taken from the staff list.
- A tampered or invalid callback is refusedA security test proves a tampered token signature is detected and the callback is refused.
- A domain not on the approved list is refusedChecked within the sign-in flow test.
- Deactivation ends access on the next requestVerified by review. The active-flag re-check runs on every request.
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.
Activity
The feature's commits cluster into about 1.7 to 2.7 hours of focused work (a lower bound); it predates the pull-request flow.
- First commit
- Feature commit: sign-in and tests
- Review fix: one change before merge
- Merged to main after a person approved
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.