fix(security): Wave 2 — fix 8 P1 frontend security & reliability issues
SEC-W-11: Remove hardcoded OAuth2 client_id/client_secret from Blazor WASM.
- Create BffAuthController (POST /api/bff/auth/login|logout, GET /api/bff/auth/session)
- BFF exchanges credentials with IS4 using server-side config (IdentityServer:ClientId/Secret)
- Add IdentityServer config block to appsettings.json / appsettings.Development.json
SEC-W-12: Migrate password grant — token exchange now happens server-side in BFF, not WASM.
- AuthService.LoginAsync() POSTs to /api/bff/auth/login (no IS4 call from WASM)
SEC-W-01: JWT in localStorage — migrate to httpOnly SameSite=Strict BFF session cookie.
- BffAuthController sets cookie on login, clears on logout
- AuthStateService no longer stores raw token (Token property removed)
- AuthService only stores non-sensitive metadata (email, role) in localStorage
- TryRestoreSessionAsync now calls GET /api/bff/auth/session instead of localStorage
- AuthForwardingHandler reads token from bff_session cookie (legacy header fallback kept)
FRONT-W-01: Token refresh not implemented — add TokenExpiry tracking + proactive refresh timer.
- AuthStateService: add TokenExpiry, OnTokenExpiring event, IDisposable Timer
- Login() schedules a Timer that fires OnTokenExpiring 2 min before expiry
FRONT-W-02: DefaultRequestHeaders race condition — use per-request HttpRequestMessage.
- PosDataService: remove AttachToken() (mutated shared DefaultRequestHeaders)
- All HTTP helpers (PostAsync, PutAsync, PostAndGetAsync, GetListFromApiAsync,
GetObjectFromApiAsync) now use HttpRequestMessage per-request
- Auth handled automatically by browser cookie (same-origin, httpOnly BFF cookie)
FRONT-C-04: No route guard on AdminLayout — add auth redirect.
- AdminLayout.OnAfterRenderAsync: after TryRestoreSessionAsync, redirect to /auth/login
if still unauthenticated (with returnUrl param)
FRONT-C-05: shopId not validated against user permissions — add BFF verification.
- AdminLayout: call PosData.GetShopByIdAsync(shopId) after detecting shop context
- Redirect to /admin if BFF returns null (403/404 = no access, prevents IDOR)
- Populate _shopName/_shopCategory from verified backend data (not just URL)
SEC-W-13: No CDN SRI for Lucide icons — add integrity hash + crossorigin attribute.
- index.html: add integrity="sha256-NBFpKCDLjUdUP2lJaqJf1gOjWPRJgEb0HFCKWjNCIQ4="
crossorigin="anonymous" to lucide@0.468.0 script tag
Co-Authored-By: Paperclip <noreply@paperclip.ing>