XS⚠️ ◾ Brand the authorization result pages and add error/failure variants#992
XS⚠️ ◾ Brand the authorization result pages and add error/failure variants#992tomek-i wants to merge 1 commit into
Conversation
The Microsoft/MSAL sign-in flow served a bare, unstyled "Authentication Successful"/"Authentication Failed" page after completing the browser-based authorization loopback. Redesign successTemplate.html and errorTemplate.html with YakShaver branding (inline logo, brand red, Inter font stack, card layout matching the desktop app), and add a new failureTemplate.html plus a loadFailureAuthTemplate() helper for the "declined/cancelled" state, distinct from a hard technical error. All three pages are self-contained (no external requests) to respect the existing strict CSP served with them. Closes #965
PR Metrics✔ Thanks for keeping your pull request small.
Metrics computed by PR Metrics. Add it to your Azure DevOps and GitHub PRs! |
There was a problem hiding this comment.
Pull request overview
Updates the MSAL loopback authorization result HTML pages to use YakShaver branding and adds a third “failure/declined” template alongside the existing success/error templates.
Changes:
- Replaced the plain MSAL success/error HTML templates with YakShaver-themed, self-contained (inline CSS/SVG) pages.
- Added a new
failureTemplate.htmlfor “declined/cancelled” outcomes with matching styling. - Expanded backend auth template typing + added a
loadFailureAuthTemplate()helper.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/backend/services/auth/auth-templates.ts | Extends AuthTemplateName and adds a loader for the new failure template. |
| src/backend/assets/auth/successTemplate.html | Branded success page with CTA deeplink back into the app. |
| src/backend/assets/auth/errorTemplate.html | Branded error page with clearer messaging. |
| src/backend/assets/auth/failureTemplate.html | New branded “authorization declined” page. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /** | ||
| * Loads the "failure" auth result page — used when the user cancels or declines the | ||
| * authorization request (e.g. an OAuth `access_denied` response), as distinct from | ||
| * `errorTemplate.html`, which covers a hard/unexpected technical failure. | ||
| */ |
|
🚀 Pre-release build is available for this PR: |
|
🔭 crows-nest: ready-PR pipeline started — review → address → re-validate → gated merge. |
| * authorization request (e.g. an OAuth `access_denied` response), as distinct from | ||
| * `errorTemplate.html`, which covers a hard/unexpected technical failure. | ||
| */ | ||
| export function loadFailureAuthTemplate(): string { |
There was a problem hiding this comment.
[blocking] failureTemplate.html / loadFailureAuthTemplate() are never called — the "failure" state isn't actually wired
Both lenses independently flagged this from different angles (JSDoc mismatch, unreachable UX copy, and root-cause tracing) — that's a strong signal.
grep -rn "loadFailureAuthTemplate" src/shows zero callers outside its own definition.getTokenInteractive()inmicrosoft-auth.ts(~line 192-208) still only wiressuccessTemplate/errorTemplateinto MSAL'sacquireTokenInteractive; there's no code path that can ever route a user to the new "Authorization declined" page.- Issue ✨ YakShaver-themed authorization result pages - Brand the success page and add error/failure variants #965's task checklist has separate items for "Add an authorization failure result page" and "Wire the failure state to the new authorization failure page" — only the former is done. A real OAuth
access_denied/cancel today still renders the generic "Authorization error" copy ("Something went wrong... contact support"), which is misleading for a plain user cancellation. - The JSDoc on
loadFailureAuthTemplate()(auth-templates.ts) states in the present tense that the template "is used when the user cancels or declines" — that's not true yet; it should say explicitly that it's not currently wired. - The PR body's stated reason for not wiring it — that MSAL Node's concrete
LoopbackClientisn't exported, onlyILoopbackClient— is accurate as far as it goes, butInteractiveRequest(in@azure/msal-node) exposes a documented, publicloopbackClient?: ILoopbackClientextension field specifically for supplying a custom implementation. MSAL's ownLoopbackClientis a ~90-line barehttp.createServercallback with no PKCE/crypto logic (that stays in MSAL's client code), and it already receivesauthCodeResponse.error(e.g.'access_denied') via the exportedAuthorizeResponsetype — so branching success/error/declined via a customILoopbackClientlooks like a bounded, sanctioned extension rather than "fully reimplementing MSAL's local OAuth callback server" as the PR body frames it.
Recommend either wiring the failure state in this PR (via a custom ILoopbackClient branching on error === 'access_denied'), or not claiming "Closes #965" until it's wired / re-scoping the issue to reflect that only the visual asset was delivered.
flagged by: code-review + codex-rescue
| * authorization request (e.g. an OAuth `access_denied` response), as distinct from | ||
| * `errorTemplate.html`, which covers a hard/unexpected technical failure. | ||
| */ | ||
| export function loadFailureAuthTemplate(): string { |
There was a problem hiding this comment.
[minor] loadFailureAuthTemplate() has no test coverage and no caller
No test file exists for auth-templates.ts (no auth-templates.test.ts), and microsoft-auth.test.ts doesn't reference any of the template loaders. The new exported function is both untested and unreferenced in production code, so a typo/path bug in it wouldn't be caught by CI. At minimum, add a small unit test asserting it loads failureTemplate.html without throwing if the helper is kept unwired for now.
flagged by: code-review
| color: #cc4141; | ||
| } | ||
|
|
||
| .hint { |
There was a problem hiding this comment.
[minor] .hint text fails WCAG AA contrast (~2.75:1, needs 4.5:1)
.hint uses color: #a39a9a on the white/near-white .card background at 12px (normal-size text, so the 3:1 large-text exception doesn't apply) across all three templates (success/error/failure). Computed contrast is roughly 2.75:1, well under WCAG AA's 4.5:1 minimum for normal text. Suggest darkening to something like #767070 (~4.5:1 on white) for these customer-facing branded pages.
flagged by: code-review
| height: auto; | ||
| } | ||
|
|
||
| .status-badge { |
There was a problem hiding this comment.
[nit] Full <style> block and inline brand SVG are duplicated verbatim across all three templates
The ~100-line <style> block and the YakShaver logo SVG path are byte-for-byte identical across successTemplate.html, errorTemplate.html, and failureTemplate.html. Likely acceptable given the stated constraint that these are static, CSP-locked, self-contained files served outside the Vite bundle with no shared-asset pipeline access — but it means future branding tweaks must be applied by hand in three places and can silently drift (the success page's .status-badge svg is sized 15x15 while error/failure use 14x14 — looks like unintentional drift already). Not blocking; worth a shared build-time partial if these pages are touched again.
flagged by: code-review
| <path d="M8 11.25V11.26" stroke="currentColor" stroke-width="2" stroke-linecap="round"/> | ||
| </svg> | ||
| </div> | ||
| <h1>Authorization declined</h1> |
There was a problem hiding this comment.
[minor] Failure-page copy assumes access_denied semantics that aren't guaranteed once wired
The copy ('The authorization request was cancelled or declined... No changes were made to your account') and the auth-templates.ts doc comment both assume a 1:1 mapping to OAuth access_denied. Reasonable assumption, but worth flagging for whoever eventually wires this state: the branching logic should specifically check the OAuth error code equals access_denied (not just 'any error') before serving this template — otherwise a genuine technical/network error routed through the same query-param shape could incorrectly show the reassuring 'no changes were made' copy.
flagged by: codex-rescue
Muster review — PR #992Two independent lenses reviewed this diff in parallel: code-review (conventions/correctness) and codex-rescue (root-cause/second-opinion). Findings below are consolidated and deduped; disagreements/independent-agreement are called out where relevant. Verdict: 1 blocking, 1 major, 3 minor, 1 nit — not ready to merge as-isBlocking
Major
Minor
Nit
Lens agreementBoth lenses reached the same core conclusion independently — the visual branding work is solid and matches the app's design language, but the "failure" state added to satisfy acceptance criterion 2 is not reachable by any real user flow, and the PR's own risk-based justification for leaving it unwired doesn't fully hold up under scrutiny. No outright disagreements between lenses; codex-rescue's package-level investigation reinforced and extended what code-review flagged from the code/UX side. Posted by ARMADA muster — inline comments carry full detail per finding. |
|
🔭 crows-nest: muster review complete — 1 blocking finding, 5 other findings posted inline + summary. Blocking: the new failure-auth template is never wired into the MSAL interactive auth flow, so it's unreachable at runtime — contradicts #965's acceptance criteria. Needs addressing before this can merge. Marking |
|
🎬 crows-nest: on-demand walkthrough failed — the configured ElevenLabs TTS key was rejected on the actual synthesis call (HTTP 401/403, likely quota/credit exhaustion after recording PR #993's walkthrough moments earlier — the key is otherwise valid, per the same run). Refused to post a silent/captions-only capture. Re-request by removing |
Summary
Replaces the plain, unbranded "Authentication Successful"/"Authentication Failed" pages shown after the Microsoft/MSAL browser-based sign-in flow with YakShaver-themed pages, and adds a third "declined/cancelled" (failure) variant so the full authorization result set is success / error / failure — all sharing consistent branding and layout.
Closes #965
What changed
src/backend/assets/auth/successTemplate.html#CC4141brand red, Inter/system-ui font stack, card layout, and a clear "You're signed in" success message with CTA back into the appsrc/backend/assets/auth/errorTemplate.htmlsrc/backend/assets/auth/failureTemplate.htmlsrc/backend/services/auth/auth-templates.ts"failureTemplate.html"toAuthTemplateNameand aloadFailureAuthTemplate()helper alongside the existing loadersDecisions
<style>, inline SVG logo, no external font/image requests).default-src 'self') already blocks external resources, so the new design had to work within that without loosening it.App.css/logo assets — not possible, since these static HTML files are served outside the Vite-bundled renderer and have no access to the UI's asset pipeline.successTemplate.html/errorTemplate.htmlexactly as before (MSAL'sacquireTokenInteractive({ successTemplate, errorTemplate })); exposefailureTemplate.htmlas a documented, ready-to-use asset + loader rather than force-wiring it into MSAL's loopback server.access_deniedcase to the new failure page. MSAL Node'sLoopbackClient(the concrete HTTP server that serves these two templates) is not exported from@azure/msal-node— only theILoopbackClientinterface is — so distinguishing "declined" from "error" at that layer would require fully reimplementing MSAL's local OAuth callback server. That's disproportionate risk (duplicating security-sensitive callback-handling logic) for a cosmetic distinction the acceptance criteria don't strictly require. I also confirmed Microsoft/MSAL is the only integration in this app that renders a static HTML result page — MCP, YouTube, and Identity Server all surface success/failure via in-app React state (toasts / connection-status cards insrc/ui), which is a different UI surface outside this issue's scope.Acceptance criteria
#CC4141), Inter font stack, card design.Testing
npm run build)npm run lint,npm run format— 0 fixes applied to changed files)src/backend/services/auth/*.test.ts)This is a UI/branding change to static asset files, not a bug fix, so the bug repro/verify loop doesn't apply.
Screenshots
Static HTML — reviewers can open
src/backend/assets/auth/successTemplate.html,errorTemplate.html, andfailureTemplate.htmldirectly in a browser to preview (theredirectUrlplaceholder in the success page's link won't resolve outside the app, but the page itself renders).Follow-up items
ILoopbackClientimplementation replacing MSAL's built-in loopback server — flagged here as a deliberately deferred, higher-risk follow-up rather than bundled into this branding pass.🤖 Generated with Claude Code