Skip to content

Settings & login controls: MFA/passkey fixes, and new POS system - #15

Open
Ruhanpaco wants to merge 13 commits into
mainfrom
rp-dev
Open

Settings & login controls: MFA/passkey fixes, and new POS system#15
Ruhanpaco wants to merge 13 commits into
mainfrom
rp-dev

Conversation

@Ruhanpaco

@Ruhanpaco Ruhanpaco commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Login & MFA

  • Fixes LoginWithMfaAsync never issuing a JWT after a correct MFA code — the two-step MFA login flow was silently broken end-to-end.
  • Fixes WebAuthn rpId hardcoded to "localhost" for passkey registration/login, which would break passkeys on any real deployment domain.
  • Fixes a passkey credentialJson double-JSON-encoding bug on register-complete that would corrupt stored credentials.
  • Adds passkey transport detection (USB / NFC / Bluetooth / hybrid) with device-type labels in Settings.
  • Adds the two-step MFA UI to the login page (authenticator code entry or passkey fallback).
  • Fixes roleGuard trusting a currentUser that was loaded without roles yet on page refresh.
  • Fixes a roleGuard double-navigation race where an expired/invalid token on a role-protected page could strand the user on "Not Found" instead of sending them to log back in.
  • Wires roleGuard's "authenticated but wrong role" case to the /error/access page (previously built but never actually linked to anywhere) instead of the generic /notfound.
  • Removes dead publicRoutes allowlist code in authGuard that could never match.

Settings & error pages

  • Redesigns the MFA setup, passkey management, and recovery codes UI in User Settings.
  • Consolidates /auth/access and /auth/error into a single /error route module.
  • Restyles the 404 page to match the new auth/settings visual language.

New POS system

  • Terminal, Inventory, Customers, Shifts, Analytics, Payment Logs, and Operators pages, with a redesigned button/card hierarchy, loading and empty states throughout.
  • Backend: categories, products, customers, orders, shifts, payment/donation logging, analytics.
  • Orders are now linked to shifts via ShiftId (previously inferred from timestamps), stock is decremented on sale, an open shift is enforced server-side before a sale can be completed, and a fixed an IDOR that let any authenticated user end another operator's shift by guessing its ID.

Responsive & polish fixes

  • All 40 modal dialogs across the app (POS, projects, announcements, profile, expenses, etc.) now collapse to 95vw on mobile instead of overflowing the viewport at a hardcoded pixel/rem width.
  • Fixed p-datepicker inside dialogs closing the whole dialog instead of letting you scroll/select a date — missing appendTo="body" caused the calendar overlay to fight the dialog's own scroll handling.

Test plan

  • Backend builds clean (dotnet build), new EF Core migrations (AddShiftIdToPosOrder, etc.) generated and reviewed
  • Frontend builds clean (ng build), all POS routes compile as separate lazy chunks
  • Manual smoke test: login → MFA code/passkey → dashboard
  • Manual smoke test: register a passkey, sign out, sign back in with it
  • Manual smoke test: start a shift, sell a product, confirm stock drops and end-shift totals/over-short are correct

🤖 Generated with Claude Code

Ruhan Pacolli and others added 9 commits July 6, 2026 15:37
…d POS system

Login & MFA:
- Fix LoginWithMfaAsync never issuing a token after successful code verification
- Fix WebAuthn rpId hardcoded to "localhost", breaking passkeys off-localhost
- Fix passkey credentialJson double-JSON-encoding on register-complete
- Add passkey transports (USB/NFC/BLE/hybrid) with device-type detection and labels
- Add two-step MFA UI to the login page (code entry or passkey fallback)
- Fix roleGuard trusting a partially-loaded currentUser (missing roles) on refresh

Settings & error pages:
- Redesign MFA setup, passkey management, and recovery codes UI in user-settings
- Consolidate /auth/access and /auth/error into a single /error route module
- Restyle 404 page to match the new auth/settings visual language

POS system:
- New POS terminal, inventory, customers, shifts, analytics, payment logs,
  and operators management pages
- Backend: categories, products, customers, orders, shifts, payment/donation
  logging, analytics
- Link orders to shifts via ShiftId (was inferred from timestamps), decrement
  stock on sale, enforce an open shift server-side, and fix an IDOR that let
  any user end another operator's shift

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Fix roleGuard double-navigating (to both /auth/login and /notfound) when
  a stale/expired token is present on a role-protected page — the second
  navigate was racing the logout() redirect and could strand the user on
  a misleading "Not Found" instead of sending them to log back in
- Wire roleGuard's "authenticated but wrong role" cases to /error/access
  (a fully-built page that was never actually linked to) instead of the
  generic /notfound page
- Remove dead publicRoutes allowlist in authGuard — it can never match
  since authGuard is only ever attached to /dashboard and /landing

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Members can request to buy something (item name, reason, link, price,
quantity, needed-by date); Admin/Leader board members approve or reject
with an optional rejection reason.

Backend:
- PurchaseRequest entity + PurchaseRequestStatus enum (Pending/Approved/
  Rejected), EF config, AddPurchaseRequests migration
- IPurchaseRequestService/PurchaseRequestService (submit, list-own,
  board queue, approve, reject) with status guards
- PurchaseRequestsController: create + view-own for any authenticated
  user; list/approve/reject gated to Admin,Leader
- Consolidated per-feature domain events + notification handlers under
  DomainEvents/Purchasing/ (reuses the shared INotificationService):
  board notified on submit, submitter notified on approve/reject
- 3 new NotificationType cases; DI wired in Program.cs

Frontend:
- purchase-requests.service.ts (thin HttpClient wrapper)
- Purchase Requests page (submit + my requests) for everyone
- Purchase Approvals page (approval queue) gated to board
- Routes + Purchasing menu section (approvals hidden from non-board)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Every p-dialog in the app used a hardcoded pixel/rem width (400px-70rem)
with no breakpoints, so on phones they overflowed the viewport and caused
horizontal scrolling.

- Added [breakpoints] to all 40 fixed-width dialogs, following the
  existing house convention ({ '575px': '95vw' }); wider dialogs
  (>=780px: project/announcement/profile editors) also get an
  intermediate { '1199px': '75vw' } step
- Stacked the side-by-side Price/Quantity and Price/Stock field rows
  in the purchase-request and POS product forms on mobile
  (flex-col sm:flex-row)

Verified at 375x812: dialog renders 356px wide with even margins and
document scrollWidth stays at 375 (no horizontal overflow).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
p-datepicker without appendTo="body" renders its calendar overlay inline
within the dialog's DOM, where the dialog's own scroll/overflow/stacking
context interferes with the overlay's touch handling — scrolling inside
the calendar to pick a date gets misread as an outside-click and closes
the whole dialog.

Affected the new "Needed By" field (purchase-requests.ts) and the
pre-existing "Add Transaction" date field (expenses.ts). Fixed both with
appendTo="body", the same convention already used correctly for every
other in-dialog datepicker in the app (projects.ts, project-details.ts,
course-portal.ts, dashboard.ts, elections.ts — all audited, all clean).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Ruhanpaco
Ruhanpaco requested a review from Daxrsa September 3, 2026 10:52
R.paco and others added 3 commits September 4, 2026 14:15
20260716000000_UpdatePosSystem shipped without a [Migration] attribute (and no
Designer file), so EF Core never ran it: PosCustomers kept its old Name/Phone
columns while the model expected FirstName/LastName, and every POS customer /
analytics query threw "42703: column p.FirstName does not exist".

Add [DbContext] + [Migration] the same way the other inline-attribute migrations
declare themselves.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Access control (extends the existing RFID card feature):
- Doors, ESP32 lock controllers (IP allow-list + shared secret + optional
  source-IP enforcement), NFC-card and Aliro / Apple Home Key credentials.
- Credential lifecycle Pending -> Active -> Declined / Disabled / Revoked; the
  door only opens for an Active credential, and a Home Key must be provisioned
  to the member's device before it can be accepted.
- Webhook for the lock: POST /api/access/verify and /api/access/event,
  authenticated by the device's X-Device-Key header.
- IAccessProvisioningService is the single seam for real lock integration
  (Matter/Aliro SetUser + SetCredential); the default impl fans out to the
  registered devices over HTTP and is fully tolerant of unreachable hardware.
- Dedicated AccessLog for every unlock, denial, credential change and
  provisioning attempt, plus the generic audit log for admin actions.
- Admin API: /api/AccessDoors, /api/AccessDevices, /api/AccessLogs, and new
  endpoints on /api/RfidCards (assign / accept / decline / disable / enable /
  provision-homekey / doors / logs). Angular: Access Control section with
  Credentials, Doors, ESP32 Devices and Access Logs pages.

Member badge codes:
- Every member gets a stable random code like FOSS-K7M2QX9P (unambiguous
  alphabet). Assigned on insert by a SaveChanges interceptor (covers every
  creation path); DbSeeder back-fills existing members on startup.
- Surfaced on the profile page, the sortable Users list, each credential row,
  a memberCode JWT claim, and the user DTOs.

Full reference in docs/ACCESS_CONTROL.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- app.menu.ts: flat always-open sections become labelled groups (Menu / Admin)
  of collapsible category dropdowns built from small helpers; empty categories
  are dropped. Auto-expands the category holding the current route, accordion
  behaviour otherwise.
- app.menuitem.ts: drop the stray `providers: [LayoutService]` — it gave every
  menu item its own LayoutService instance, breaking the shared menuSource$ bus
  so nested submenus could never auto-expand. LayoutService is providedIn:'root'.
- Add a root README; remove the stale DOCKER.md (wrong ports/commands, now
  covered by the README). Add docker-compose.local.yml for host port mappings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Door access control + member badge codes + sidebar redesign
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.

1 participant