Skip to content

feat: v0.20 console: step-up on every admin write, staged policy page, v0.20 audit events - #314

Merged
Bccorb merged 7 commits into
mainfrom
feat/v020-console
Oct 26, 2026
Merged

Bccorb merged 7 commits into
mainfrom
feat/v020-console

Conversation

@Bccorb

@Bccorb Bccorb commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Console support for the v0.20 Seamless Auth API.

Closes #310
Closes #295

#310: step up before every admin write

  • Organization create and update (handleCreateOrganization, handleUpdateOrganization) now call ensureStepUp() first.
  • Audit of every write in the console: saving your own profile on /profile (PATCH /admin/users/:userId) was also unguarded and now steps up. Every other write the API guards already stepped up (user create, update, delete, device replacement, session revoke and revoke all, system config save, OAuth provider create, update, toggle and delete, organization delete, member add, update and remove, audit export). The console has no user import or provider retirement UI. Enrollment invites and policy draft save and discard are not step-up routes on the API, so they stay on the role alone.
  • apiFetch handles 403 { error: "step_up_required" } centrally (src/lib/api.ts, src/lib/stepUpRequired.ts, src/components/StepUpRequiredHandler.tsx): it runs the step-up prompt (forcing a fresh status check, since the cached one is known to be stale) and retries the request once. Concurrent refusals share one prompt. A declined prompt shows "This action needs a recent step-up verification..." instead of "You do not have permission".
  • ApiError now carries the API's error code (code).

#295: staged policy UI

New Sign-in Policy page at /policy, in the sidebar:

  • Published version against the draft, as a per-setting diff (object settings compared field by field), with a warning when the draft was started from an older version.
  • Edit, save and discard the draft. The editor reuses the System Configuration field controls, moved to src/components/ConfigFields.tsx and src/lib/configFields.ts (no behaviour change there).
  • Dry run of the saved draft, or of unsaved edits, over the last 1 to 30 days: sign-ins and accounts evaluated, accounts the draft would refuse (with reasons), accounts with no remaining method (usersLockedOut, listed first), break-glass accounts counted separately, and a cap notice.
  • Publish with expectedVersion. A 409 policy_version_conflict reloads the policy and history and tells the operator.
  • Version history with version, source, when and who, a diff of any version against the policy in force, and rollback.
  • Publish and rollback step up first. Reads and dry run need admin:read; edit, publish and rollback controls are hidden without admin:write, matching the API.
  • System Configuration: the Login Policy section says saves apply at once and are recorded as a policy version, links to Sign-in Policy, flags a staged draft, and asks for confirmation when a policy setting is saved while a draft is staged. A config save refreshes the policy queries.

Audit events

  • @seamless-auth/types bumped to ^0.31.0 (0.31.0 is the latest published version).
  • New Break-Glass, Account Recovery and Policy categories (filters and summary charts); session_evicted and admin_last_admin_change_refused join their neighbouring categories, so no 0.31.0 event type falls into Other.
  • Event tables on Events, User Detail and Security show a readable name with the raw type beneath it. break_glass_sign_in, break_glass_enrollment, and any event with metadata.severity: "high" get a High severity badge and count as security signals.

Contract notes

  • The dry run response has no cap flag. The API strips capped before answering and only records it in the policy_dry_run event. The page shows the cap notice when usersEvaluated + breakGlassAccounts reaches 10,000, and reads a capped field if a later API sends one.
  • There is no usersWithNoRemainingMethod field; the console uses usersLockedOut and the per-account lockedOut.
  • publishedBy and updatedBy are user ids only, so the history links to the user rather than showing an email.

Checks

$ npm run typecheck
> tsc -b

$ npm run lint
> eslint .

$ npm run format:check
Checking formatting...
All matched files use Prettier code style!

$ npm test
 Test Files  99 passed (99)
      Tests  669 passed (669)

$ npm run build
✓ built in 184ms

$ npm run check:types-current
seamless-auth-admin-dashboard dependencies: @seamless-auth/types "^0.31.0" is current (latest 0.31.0).

New Playwright specs are in e2e/tests/policy.spec.ts (including a publish refused with step_up_required and retried), and the policy routes are seeded in e2e/personas.ts. The pinned Chromium build is not installed on this machine, so e2e ran in CI only: test (Node 22), test (Node 24), test (Node latest) and e2e all pass on 0bca7ae.

Bump @seamless-auth/types to ^0.31.0, which adds the recovery, account
recovery, break-glass, policy and step-up event types.

- New Break-Glass, Account Recovery and Policy categories, so the event
  filters and summary charts no longer drop these into Other.
- session_evicted and admin_last_admin_change_refused join their
  neighbouring categories.
- Event tables show a readable name with the raw type beneath it.
- break_glass_sign_in and break_glass_enrollment, and any event whose
  metadata carries severity "high", show a High severity badge and count
  as security signals on the Events page.
…equired

The API now requires a fresh step-up on every admin write.

- Organization create and update, and saving your own profile (PATCH
  /admin/users/:userId), now step up first like the other writes.
- apiFetch handles 403 step_up_required centrally: it opens the step-up
  prompt and retries the request once. Concurrent refusals share one
  prompt. A declined prompt reports that verification is needed instead
  of "You do not have permission".
- ApiError carries the API's error code so callers can branch on it.
The policy page edits the same sign-in policy keys as System
Configuration, so the field controls and their option lists move to
src/components/ConfigFields.tsx and src/lib/configFields.ts. No
behaviour change.
Adds the Sign-in Policy page at /policy for the API's staged policy routes.

- Published version against the draft, as a per-setting diff, with a
  warning when the draft was started from an older version.
- Edit and save the draft, or discard it.
- Dry run of the draft, or of unsaved edits, over the last 1 to 30 days:
  refused sign-ins and accounts, accounts with no remaining method,
  break-glass accounts counted apart, and a notice when the run may
  have reached the API's 10,000 account limit.
- Publish with expectedVersion. A 409 policy_version_conflict reloads
  the policy and tells the operator.
- Version history with source, time and author, a diff of any version
  against the policy in force, and rollback.
- Publish and rollback step up first. Writes are hidden from read-only
  admins, who can still review and dry run.
PATCH /system-config/admin still applies policy keys at once, and the API
records each such save as a config_patch policy version.

- The Login Policy section says so and links to Sign-in Policy.
- A staged draft is flagged, and saving a policy setting while one is
  staged asks for confirmation, since publishing the draft later would
  replace the saved settings.
- A save refreshes the policy queries so the Sign-in Policy page shows
  the new version.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: step up before every admin write and prompt on step_up_required feat: review, publish and roll back policy changes in the console

1 participant