All features

Feature breakdown · built with CraftHQ

SecurityStaff portal

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

Estimated ~8 h

2 people incl. the product owner, requirements to merge

Estimate at this build's pace, not a measurement of this feature

PR review & mergebuilt before pull requests were in use

Staff sign in with the Microsoft work account they already use, so there is no second password to manage.

Size: 10 FP · In-house IFPUG-style count, cross-checked by a second counter; not certified. Range 7 to 12. · How we counted 10 FP

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)

Whole-team time at this build's pace2 people incl. the product owner, requirements to merge
~8 h

Estimate at this build's pace, not a measurement of this feature

A typical team, whole lifecycleRequirements to release · middle half of 512 comparable projects (Java, Agile), ISBSG 2023
~68 to 94 habout 1.5–2.5 person-weeksmedian ~79 h

Hands-on build time: ~1.7 to 2.7 h, measuredhands-on build time, a lower bound from commit activity

grey band = a typical team, middle halfhatched = our whole-team estimate

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

The questions are answered.
The acceptance criteria are testable.
The size is estimated.
The scope is checked for splitting.
The product owner has signed off.

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

Every acceptance criterion is checked; where a scenario has a dedicated test it is cited, and where it was verified by review that is stated.
This feature predates the three-check pull-request flow; it was reviewed and approved by a person before merging to main.
A person approved the merge.

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

People
Staff membersigns in
Client (untrusted)
Sign-in screenshows the Microsoft button
Your app
Sign-in handlervalidates and resolves the user
Staff listroles and active status
Sessionsstarted on success
Audit logrecords every sign-in
External services
Identity providerMicrosoft work accounts
  • 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
Simplified.

Sequence

Browserthe staff member
App serverthe sign-in handler
Staff list + auditread and append
Identity providerMicrosoft work accounts
  • 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
Simplified.

Infrastructure

Client (untrusted)
Web appthe sign-in screen, in the browser
Your app
App serverhandles sign-in; authenticates and pins the tenant
Databaseper-tenant isolation
External services
Identity providerMicrosoft work accounts
  • 1sign-in request (authenticate + tenant check at the edge)
  • 2authorization-code flow
  • 3read staff list, append audit
Simplified.

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 @security
Feature: Single sign-on with Microsoft
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
Staff 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 configured
And the staff list is maintained by an administrator
And each staff member is matched to their directory account, pinned to its immutable object id on first sign-in
Rule: Microsoft proves who the person is; the staff list decides access
Scenario: A listed, active staff member signs in
Given a staff member has an active Microsoft work account on an approved domain
And that person is on the staff list
When they sign in with Microsoft
Then they land on the dashboard for their role
And the sign-in is recorded
Scenario: The role decides the landing page
Given an administrator has set a staff member's role in the staff list
When that staff member signs in
Then they land on the dashboard for that role
Rule: Only active, listed accounts may sign in
Scenario: An account not on the staff list is refused
Given a person signs in with Microsoft but is not on the staff list
When the application checks them against the staff list
Then they are refused and returned to sign-in
And no account is created
Scenario: A sign-in from a domain that is not approved is refused
Given a person signs in with Microsoft from a domain that is not on the approved list
When the application checks the domain
Then they are refused before any account is resolved
Scenario: An account disabled in the directory cannot sign in again
Given a staff member has been disabled in the Microsoft directory
When they try to sign in
Then Microsoft refuses the sign-in
And on return the application shows the returned error
Rule: Access ends when an administrator deactivates the user in the staff list
Scenario: Deactivation ends access on the next request
Given a staff member is signed in
And an administrator deactivates them in the staff list
When the staff member takes their next action
Then the action is refused because the active flag is re-checked on every request
And their session ends without waiting for the idle timer
Scenario: A role change ends the open session
Given a staff member is signed in
When an administrator changes their role
Then their session ends on the next request, as for deactivation
Rule: A session left idle is signed out
Scenario: Idle timeout
Given a staff member has been idle for the configured timeout
When they take their next action
Then they are signed out
And returned to sign-in with a "session expired" notice
Rule: The sign-in callback is validated before any session starts
@edge-cases
Scenario Outline: A tampered or invalid callback is refused
Given a person returns from Microsoft to the sign-in callback
And the callback is "<condition>"
When the application validates the callback
Then sign-in is refused and no session is started
Examples:
| 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 proceeds
Given a person returns from Microsoft to the sign-in callback
And the return path points off-site
When the application validates the callback
Then the return path is sanitised to an internal application path
And 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.

AMicrosoft only. Remove the password form, forgot-password and reset. · recommended
BKeep a password fallback alongside Microsoft sign-in.
Decided: Microsoft only (option A).

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.

AIdentity only. Roles and access come from the staff list; anyone not listed is refused. · recommended
BTrust the identity provider's groups for access as well as identity.
Decided: Resolve only, fail-closed; roles come from the app's staff list (option A).

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.

ANo. Leave lockout to the identity provider; a locked account is refused at Microsoft, and on return the app shows the error. · recommended
BCount failures and lock accounts in the app.
Decided: Lockout stays with the identity provider (option A).

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 account
    User input
    6
  • Record the directory identity on first sign-in
    User input
    4
  • Total
    10
10 function points (range 7–12). IFPUG-style count: each user action, lookup, output and stored data set, weighted by complexity. Technical plumbing is excluded, and a data store is counted once in the feature that owns it.

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 in
    An 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 page
    The sign-in flow test checks the role and landing page are taken from the staff list.
  • A tampered or invalid callback is refused
    A security test proves a tampered token signature is detected and the callback is refused.
  • A domain not on the approved list is refused
    Checked within the sign-in flow test.
  • Deactivation ends access on the next request
    Verified 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.

Measured from your own git history
Every pull request tested before review
A person approves every merge