to navigate Enter to open "…" all these words ANDOR to combine

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: Admin is the only elevated role (registration creates a Customer). 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, the two password-reset pages, and the public catalog.

flowchart TD
    subgraph Auth["Authentication"]
        Login["/login<br/>Login"]
        Register["/register<br/>Register"]
        ForgotPassword["/forgot-password<br/>Request Reset Link"]
        ResetPassword["/reset-password<br/>Set New Password"]
    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

    Login -->|Forgot password link| ForgotPassword
    ForgotPassword -->|back to sign in| Login
    ForgotPassword -.->|reset link in the email| ResetPassword
    ResetPassword -->|on success| Login

    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.

/forgot-password and /reset-password are shipped by the framework UI package and carry no [Authorize] attribute. The dashed edge is the reset email: the link carries the email and the token in the query string, and both fields stay editable so a recipient can paste the token by hand.


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

/orders lists only the caller's own orders (ownership Specification row-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 the OrphanOrderRecovery component 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:

  1. Page-level route guards. The 14 admin pages carry [Authorize(Roles = "Admin")] and /profile / /orders carry [Authorize]. SSR session-cookie auth (ADR-022, mmca_auth_access HttpOnly 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 by Catalog/Sales/IdentityRouteAuthorizationTests in CI (commit c4adff2).
  2. API resource ownership (ADR-033). OwnerOrAdminFilter 403s requests whose customer_id claim mismatches the owner parameter, and OwnershipHelper.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.
  3. In-page conditionals. AuthorizeView hides 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.