Skip to content

fix(web): suspended and offboarded tenants get their page again - #164

Merged
okieselbach merged 3 commits into
mainfrom
claude/validation-access-denied-i7mpqm
Sep 28, 2026
Merged

okieselbach merged 3 commits into
mainfrom
claude/validation-access-denied-i7mpqm

Conversation

@okieselbach

Copy link
Copy Markdown
Owner

Since the typed error envelope (7cef2ea, 2026-09-05) the policy middleware's suspension gate
answers auth/me with { error: , code: "TenantSuspended" } before AuthFunction runs.
AuthContext still compared error === "TenantSuspended", so every caller without platform scope
of a suspended or offboarding tenant got the claims-only fallback and a 403 on every call
instead of the suspended page or the farewell feedback form.

  • lib/authMeRefusal.ts classifies auth/me's 403 in both shapes: the envelope (code in code,
    text in error) and AuthFunction's pre-envelope body ({ error: , message }) that the
    activation gate and the platform-caller suspension still write. TenantSuspended is checked
    against the generated ApiErrorCode vocabulary.
  • AuthContext parses the refusal with apiErrorFromResponse; its JSON-parse ratchet drops to 2.
  • Tests: both shapes, the offboarding text, PendingActivation / PrivatePreview, non-matches.

Web only; works against the deployed backend.

Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01FfPq5odfetjR7ExY8K8gEu

Since the typed error envelope (7cef2ea, 2026-09-05) the policy middleware's suspension gate
answers auth/me with { error: <text>, code: "TenantSuspended" } before AuthFunction runs.
AuthContext still compared error === "TenantSuspended", so every caller without platform scope
of a suspended or offboarding tenant got the claims-only fallback and a 403 on every call
instead of the suspended page or the farewell feedback form.

- lib/authMeRefusal.ts classifies auth/me's 403 in both shapes: the envelope (code in `code`,
  text in `error`) and AuthFunction's pre-envelope body ({ error: <code>, message }) that the
  activation gate and the platform-caller suspension still write. TenantSuspended is checked
  against the generated ApiErrorCode vocabulary.
- AuthContext parses the refusal with apiErrorFromResponse; its JSON-parse ratchet drops to 2.
- Tests: both shapes, the offboarding text, PendingActivation / PrivatePreview, non-matches.

Web only; works against the deployed backend.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FfPq5odfetjR7ExY8K8gEu
The Cloud PC, Intune Enrollment and Unrestricted Mode toggles set their state before
saveConfiguration runs, and its failure branch never re-synced it: after a refused save the
toggle stayed on although nothing was stored.

saveConfiguration now syncs the five validation gates and Unrestricted Mode through one
helper, to the merged row on success (unchanged) and to the loaded row on failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FfPq5odfetjR7ExY8K8gEu
…how Graph's refusal

Assigning Microsoft Graph application permissions needs Global Administrator or Privileged
Role Administrator. The grant script and the app-switch add-on step also named Cloud
Application Administrator, which Microsoft excludes for Microsoft Graph; such an admin got
"Response status code does not indicate success: Forbidden (Forbidden)" and no hint.

- Grant-AutopilotMonitorAddOn.ps1: .NOTES and the scope warning name the two roles; the
  admin-consent prerequisite moves under Requires. Get-GraphFailure reads Graph's error code
  and message from the response body in ErrorDetails (behind the headers, one of which,
  x-ms-ags-diagnostic, carries JSON itself); a 403 on grant or revoke ends the summary with
  the roles to sign in with. New text is ASCII (the script is published without BOM).
- AppHomingAddOnStep and the Optional Graph capabilities command section name the two roles
  (the latter said "tenant administrator", the portal role); the step's test pins them.

The customer docs (reference/optional-graph-permissions.md,
troubleshooting/app-registration-migration.md) still name Cloud Application Administrator and
need the same fix in the docs repo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FfPq5odfetjR7ExY8K8gEu
@okieselbach
okieselbach merged commit f920b62 into main Sep 28, 2026
8 checks passed
@okieselbach
okieselbach deleted the claude/validation-access-denied-i7mpqm branch October 2, 2026 07:20
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.

2 participants