Guides & specifications
Navigation Flow
This document maps the site navigation flow for each actor in the MMCA.Store application. Each mermaid diagram shows the pages accessible to that actor and the directional navigation links between them. (Companion to MMCA.ADC's NavigationFlow.md; same per-actor format.)
Actors
| Actor | Access Level | Identification |
|---|---|---|
| Anonymous | Public catalog browse + auth pages | Not authenticated |
| Customer | Anonymous + profile, cart/checkout, own orders | Authenticated, default Customer role (customer_id claim) |
| Admin | Full access: Catalog/Sales/Identity admin CRUD | Authenticated, Admin role |
Roles and enforcement:
Adminis the only elevated role (registration creates aCustomer). The 14 admin pages carry page-level[Authorize(Roles = "Admin")], regression-gated in CI by the three per-module*RouteAuthorizationTests(MMCA.Store.CI.slnf:39,45,51). Customer-facing data is additionally row-scoped server-side (see Authorization Model at the end), so the page gate is defense-in-depth, never the boundary.
1. Anonymous User
Pages accessible without authentication: home, login, register, and the public catalog.
flowchart TD
subgraph Auth["Authentication"]
Login["/login<br/>Login"]
Register["/register<br/>Register"]
end
subgraph Catalog["Public Catalog"]
Browse["/catalog<br/>Catalog Browse"]
ProductDetail["/catalog/{Id}<br/>Product Detail"]
end
Home["/ Store Home"]
Home -->|hero / nav menu| Browse
Home -->|auth links| Login
Home -->|auth links| Register
Login -->|on success| Home
Register -->|on success| Home
Browse -->|card click| ProductDetail
ProductDetail -->|back| Browse
ProductDetail -->|add to cart| Login
Add-to-cart on the product detail page sits inside an
AuthorizeView; an anonymous visitor is prompted to log in instead.
2. Customer (Authenticated User)
Inherits all anonymous pages. Gains the profile page, the cart drawer (a layout component, not a route), checkout, and their own orders. Unauthenticated visitors deep-linking to these pages are redirected to login (SSR session-cookie auth keeps [Authorize] enforced on fresh GETs and F5, ADR-022).
flowchart TD
subgraph Auth["Authentication"]
Login["/login<br/>Login"]
end
subgraph Catalog["Public Catalog"]
Browse["/catalog<br/>Catalog Browse"]
ProductDetail["/catalog/{Id}<br/>Product Detail"]
end
subgraph Sales["Cart and Orders"]
Cart["Cart Drawer<br/>(layout component)"]
Orders["/orders<br/>My Orders"]
OrderDetail["/orders/{Id}<br/>Order Detail"]
end
subgraph Identity["Profile"]
Profile["/profile<br/>My Profile"]
end
Home["/ Store Home"]
Home -->|nav menu| Browse
Home -->|nav menu| Orders
Home -->|nav menu| Profile
Browse -->|card click| ProductDetail
ProductDetail -->|add to cart| Cart
Cart -->|checkout| Orders
Cart -->|Stripe payment| OrderDetail
Orders -->|row click| OrderDetail
OrderDetail -->|back| Orders
/orderslists only the caller's own orders (ownershipSpecificationrow-scoping); an admin on the same route sees all orders. The order detail data is ownership-checked server-side with 404-not-403 semantics so foreign order ids do not leak existence. Abandoned Stripe payments are recovered by theOrphanOrderRecoverycomponent on return.
3. Admin
Inherits all customer pages, plus the admin CRUD surfaces for all three modules. Every page below carries [Authorize(Roles = "Admin")]; a customer deep-linking to any of them gets Forbidden on both fresh GET and F5.
flowchart TD
subgraph CatalogAdmin["Catalog Admin"]
Categories["/categories<br/>Category List"]
CategoryCreate["/categories/create<br/>Create Category"]
CategoryDetail["/categories/{Id}<br/>Category Detail"]
Products["/products<br/>Product List"]
ProductCreate["/products/create<br/>Create Product"]
ProductDetailAdm["/products/{Id}<br/>Product Detail"]
end
subgraph SalesAdmin["Sales Admin"]
Inventory["/inventory<br/>Inventory List"]
InventoryCreate["/inventory/create<br/>Create Inventory Item"]
InventoryDetail["/inventory/{Id}<br/>Inventory Detail"]
Carts["/shoppingcarts<br/>Shopping Cart List"]
CartDetail["/shoppingcarts/{Id}<br/>Shopping Cart Detail"]
OrdersAll["/orders<br/>All Orders"]
OrderDetailAdm["/orders/{Id}<br/>Order Detail"]
end
subgraph IdentityAdmin["Identity Admin"]
Customers["/customers<br/>Customer List"]
CustomerCreate["/customers/create<br/>Create Customer"]
CustomerDetail["/customers/{Id}<br/>Customer Detail"]
end
Home["/ Store Home"]
Home -->|nav menu| Categories
Home -->|nav menu| Products
Home -->|nav menu| Inventory
Home -->|nav menu| Carts
Home -->|nav menu| OrdersAll
Home -->|nav menu| Customers
Categories -->|row click| CategoryDetail
Categories -->|create| CategoryCreate
Products -->|row click| ProductDetailAdm
Products -->|create| ProductCreate
Inventory -->|row click| InventoryDetail
Inventory -->|create| InventoryCreate
Carts -->|row click| CartDetail
OrdersAll -->|row click| OrderDetailAdm
Customers -->|row click| CustomerDetail
Customers -->|create| CustomerCreate
Authorization Model
Three cooperating layers; the API is always the boundary:
- Page-level route guards. The 14 admin pages carry
[Authorize(Roles = "Admin")]and/profile//orderscarry[Authorize]. SSR session-cookie auth (ADR-022,mmca_auth_accessHttpOnly cookie) lets these attributes pass on fresh GETs, F5, and new tabs, so deep links never render a protected shell to the wrong actor. Regression-gated byCatalog/Sales/IdentityRouteAuthorizationTestsin CI (commitc4adff2). - API resource ownership (ADR-033).
OwnerOrAdminFilter403s requests whosecustomer_idclaim mismatches the owner parameter, andOwnershipHelper.GetOwnershipSpecification()row-scopes collection queries so customers only ever receive their own carts/orders. Per-mutation checks on orders return 404-not-403 to avoid leaking existence. - In-page conditionals.
AuthorizeViewhides customer-only affordances (add-to-cart) from anonymous visitors and admin-only affordances from customers; these are UX sugar on top of layers 1-2, never the enforcement.
Menu items are rendered per-role, so each actor's nav menu contains only the routes shown in their diagram above.