From 0d7915c27563dc2bc9e9b9068c7b02cda6cf9d90 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Sat, 22 Nov 2025 18:21:23 +0100 Subject: [PATCH] docs: update authentication dependency names across documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated all documentation to use correct authentication dependency names: - HTML pages: get_current_admin_from_cookie_or_header, get_current_vendor_from_cookie_or_header, get_current_customer_from_cookie_or_header - API endpoints: get_current_admin_api, get_current_vendor_api, get_current_customer_api Changes: - Updated authentication flow diagrams with correct dependency names for admin and vendor flows - Added comprehensive customer/shop authentication flow documentation - Updated cookie isolation architecture to show all three contexts (admin, vendor, shop) - Expanded security boundary enforcement diagram to include shop routes - Added customer cross-context prevention examples - Updated all code examples in frontend and backend documentation - Fixed import statements to use app.api.deps instead of app.core.auth Files updated: - docs/api/authentication-flow-diagrams.md (added customer flows) - docs/frontend/admin/page-templates.md - docs/frontend/admin/architecture.md - docs/frontend/shared/ui-components.md - docs/frontend/shared/sidebar.md - docs/development/exception-handling.md - docs/architecture/diagrams/vendor-domain-diagrams.md - docs/backend/admin-integration-guide.md - docs/backend/admin-feature-integration.md πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/api/authentication-flow-diagrams.md | 269 ++++++++++++------ .../diagrams/vendor-domain-diagrams.md | 2 +- docs/backend/admin-feature-integration.md | 12 +- docs/backend/admin-integration-guide.md | 12 +- docs/development/exception-handling.md | 2 +- docs/frontend/admin/architecture.md | 12 +- docs/frontend/admin/page-templates.md | 6 +- docs/frontend/shared/sidebar.md | 2 +- docs/frontend/shared/ui-components.md | 2 +- 9 files changed, 213 insertions(+), 106 deletions(-) diff --git a/docs/api/authentication-flow-diagrams.md b/docs/api/authentication-flow-diagrams.md index 4a78df28..75d5e4ee 100644 --- a/docs/api/authentication-flow-diagrams.md +++ b/docs/api/authentication-flow-diagrams.md @@ -3,30 +3,33 @@ ## Cookie Isolation Architecture ``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Browser β”‚ -β”‚ β”‚ -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ β”‚ Admin Area β”‚ β”‚ Vendor Area β”‚ β”‚ -β”‚ β”‚ /admin/* β”‚ β”‚ /vendor/* β”‚ β”‚ -β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ -β”‚ β”‚ πŸͺ admin_token β”‚ β”‚ πŸͺ vendor_token β”‚ β”‚ -β”‚ β”‚ Path: /admin β”‚ β”‚ Path: /vendor β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β”‚ β”‚ β”‚ β”‚ -β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ -β”‚ β”‚ ❌ No Cookie Mixing β”‚ β”‚ -β”‚ β”‚ β”‚ β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ β”‚ - β–Ό β–Ό -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Admin Backend β”‚ β”‚ Vendor Backend β”‚ -β”‚ /admin/* β”‚ β”‚ /vendor/* β”‚ -β”‚ β”‚ β”‚ β”‚ -β”‚ βœ… admin_token β”‚ β”‚ βœ… vendor_token β”‚ -β”‚ ❌ vendor_token β”‚ β”‚ ❌ admin_token β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Browser β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Admin Area β”‚ β”‚ Vendor Area β”‚ β”‚ Shop Area β”‚ β”‚ +β”‚ β”‚ /admin/* β”‚ β”‚ /vendor/* β”‚ β”‚ /shop/* β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ πŸͺ admin_token β”‚ β”‚ πŸͺ vendor_token β”‚ β”‚ πŸͺ customer_ β”‚ β”‚ +β”‚ β”‚ Path: /admin β”‚ β”‚ Path: /vendor β”‚ β”‚ token β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Path: /shop β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ +β”‚ β”‚ ❌ No Cookie Mixing β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Admin Backend β”‚ β”‚ Vendor Backend β”‚ β”‚ Shop Backend β”‚ +β”‚ /admin/* β”‚ β”‚ /vendor/* β”‚ β”‚ /shop/* β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ βœ… admin_token β”‚ β”‚ βœ… vendor_token β”‚ β”‚ βœ… customer_ β”‚ +β”‚ ❌ vendor_token β”‚ β”‚ ❌ admin_token β”‚ β”‚ token β”‚ +β”‚ ❌ customer_ β”‚ β”‚ ❌ customer_ β”‚ β”‚ ❌ admin_token β”‚ +β”‚ token β”‚ β”‚ token β”‚ β”‚ ❌ vendor_token β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## Login Flow - Admin @@ -63,15 +66,17 @@ └── API call to /api/v1/admin/vendors ──────── (Authorization: Bearer ) β”‚ β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ get_current_admin_user() β”‚ - β”‚ β”‚ - β”‚ 1. Check Auth header β”‚ - β”‚ 2. Check admin_token cookie β”‚ - β”‚ 3. Validate JWT β”‚ - β”‚ 4. Verify role == admin β”‚ - β”‚ βœ… Return User β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ HTML: get_current_admin_from_ β”‚ + β”‚ cookie_or_header() β”‚ + β”‚ API: get_current_admin_api() β”‚ + β”‚ β”‚ + β”‚ 1. Check Auth header β”‚ + β”‚ 2. Check admin_token cookie (HTML)β”‚ + β”‚ 3. Validate JWT β”‚ + β”‚ 4. Verify role == admin β”‚ + β”‚ βœ… Return User β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## Login Flow - Vendor @@ -109,54 +114,104 @@ └── API call to /api/v1/vendor/ACME/products ─ (Authorization: Bearer ) β”‚ β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ get_current_vendor_user() β”‚ - β”‚ β”‚ - β”‚ 1. Check Auth header β”‚ - β”‚ 2. Check vendor_token cookieβ”‚ - β”‚ 3. Validate JWT β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ HTML: get_current_vendor_from_ β”‚ + β”‚ cookie_or_header() β”‚ + β”‚ API: get_current_vendor_api() β”‚ + β”‚ β”‚ + β”‚ 1. Check Auth header β”‚ + β”‚ 2. Check vendor_token cookie (HTML)β”‚ + β”‚ 3. Validate JWT β”‚ β”‚ 4. Block if admin │──> ❌ Error - β”‚ 5. Verify vendor access β”‚ - β”‚ βœ… Return User β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ 5. Verify vendor access β”‚ + β”‚ βœ… Return User β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Login Flow - Customer (Shop) + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Browser β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”‚ POST /api/v1/shop/auth/login + β”‚ { email, password } + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Customer Auth Endpoint β”‚ +β”‚ β”‚ +β”‚ 1. Validate credentialsβ”‚ +β”‚ 2. Check role = customerβ”‚ +β”‚ 3. Generate JWT β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”‚ Set-Cookie: customer_token=; Path=/shop; HttpOnly; SameSite=Lax + β”‚ Response: { access_token, user } + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Browser │──────────────────────────────────────┐ +β”‚ β”‚ β”‚ +β”‚ πŸͺ customer_token (Path=/shop) β”‚ +β”‚ πŸ’Ύ localStorage.access_token β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ + β”‚ β”‚ + β”œβ”€β”€ Navigate to /shop/account/dashboard ────── + β”‚ (Cookie sent automatically) β”‚ + β”‚ β”‚ + └── API call to /api/v1/shop/orders ────────── + (Authorization: Bearer ) β”‚ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ HTML: get_current_customer_from_ β”‚ + β”‚ cookie_or_header() β”‚ + β”‚ API: get_current_customer_api() β”‚ + β”‚ β”‚ + β”‚ 1. Check Auth header β”‚ + β”‚ 2. Check customer_token cookie (HTML)β”‚ + β”‚ 3. Validate JWT β”‚ + β”‚ 4. Verify role = customer β”‚ + β”‚ βœ… Return User β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## Security Boundary Enforcement ``` - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ Request Comes In β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ What's the path? β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ β”‚ β”‚ - Starts with Starts with Starts with - /admin/* /vendor/* /api/* - β”‚ β”‚ β”‚ - β–Ό β–Ό β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ Check for: β”‚ β”‚ Check for: β”‚ β”‚ Check for: β”‚ - β”‚ - admin_token β”‚ β”‚ - vendor_token β”‚ β”‚ - Authorization β”‚ - β”‚ cookie β”‚ β”‚ cookie β”‚ β”‚ header β”‚ - β”‚ - OR Auth header β”‚ β”‚ - OR Auth header β”‚ β”‚ (required) β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ β”‚ β”‚ - β–Ό β–Ό β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ Validate: β”‚ β”‚ Validate: β”‚ β”‚ Validate: β”‚ - β”‚ - JWT valid β”‚ β”‚ - JWT valid β”‚ β”‚ - JWT valid β”‚ - β”‚ - User active β”‚ β”‚ - User active β”‚ β”‚ - User active β”‚ - β”‚ - Role = admin β”‚ β”‚ - Role != admin β”‚ β”‚ - Any role β”‚ - β”‚ β”‚ β”‚ - Has vendor β”‚ β”‚ (depends on β”‚ - β”‚ β”‚ β”‚ access β”‚ β”‚ endpoint) β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ β”‚ β”‚ - β–Ό β–Ό β–Ό - βœ… Allowed βœ… Allowed βœ… Allowed + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Request Comes In β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ What's the path? β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ β”‚ β”‚ β”‚ + Starts with Starts with Starts with Starts with Starts with + /admin/* /vendor/* /shop/* /api/* (public) + β”‚ β”‚ β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό β–Ό β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Check for: β”‚β”‚ Check for: β”‚β”‚ Check for: β”‚β”‚ Check for: β”‚β”‚ No Auth β”‚ +β”‚ - admin_token β”‚β”‚ - vendor_token β”‚β”‚ - customer_ β”‚β”‚ - Authorizationβ”‚β”‚ Required β”‚ +β”‚ cookie β”‚β”‚ cookie β”‚β”‚ token cookie β”‚β”‚ header β”‚β”‚ β”‚ +β”‚ - OR Auth β”‚β”‚ - OR Auth β”‚β”‚ - OR Auth β”‚β”‚ (required) β”‚β”‚ Public pages β”‚ +β”‚ header β”‚β”‚ header β”‚β”‚ header β”‚β”‚ β”‚β”‚ & assets β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Validate: β”‚β”‚ Validate: β”‚β”‚ Validate: β”‚β”‚ Validate: β”‚ +β”‚ - JWT valid β”‚β”‚ - JWT valid β”‚β”‚ - JWT valid β”‚β”‚ - JWT valid β”‚ +β”‚ - User active β”‚β”‚ - User active β”‚β”‚ - User active β”‚β”‚ - User active β”‚ +β”‚ - Role = admin β”‚β”‚ - Role != adminβ”‚β”‚ - Role = β”‚β”‚ - Any role β”‚ +β”‚ β”‚β”‚ - Has vendor β”‚β”‚ customer β”‚β”‚ (depends on β”‚ +β”‚ β”‚β”‚ access β”‚β”‚ β”‚β”‚ endpoint) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό β–Ό + βœ… Allowed βœ… Allowed βœ… Allowed βœ… Allowed ``` ## Cross-Context Prevention @@ -229,6 +284,55 @@ Admin cookie sent to vendor route: "Vendor authentication required" ``` +``` +Customer trying to access admin route: +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ User: customer@example.com (role: customer)β”‚ +β”‚ Token: Valid JWT with customer role β”‚ +β”‚ Request: GET /admin/dashboard β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ get_current_admin_ β”‚ + β”‚ from_cookie_or_header β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + Check: role == "admin"? + β”‚ + β–Ό No + ❌ AdminRequiredException + "Admin privileges required" +``` + +``` +Customer cookie sent to shop route (allowed): +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Cookie: customer_token= (Path=/shop)β”‚ +β”‚ Request: GET /shop/account/orders β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + Browser checks cookie path + β”‚ + β–Ό + Path /shop matches /shop + β”‚ + β–Ό + βœ… Cookie SENT automatically + β”‚ + β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ get_current_customer_ β”‚ + β”‚ from_cookie_or_header β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + βœ… Customer authenticated + Page loads successfully +``` + ## Cookie Lifecycle ``` @@ -236,9 +340,9 @@ LOGIN β”‚ β”œβ”€β”€ Server generates JWT β”œβ”€β”€ Server sets cookie: - β”‚ β€’ Name: admin_token or vendor_token + β”‚ β€’ Name: admin_token, vendor_token, or customer_token β”‚ β€’ Value: JWT - β”‚ β€’ Path: /admin or /vendor + β”‚ β€’ Path: /admin, /vendor, or /shop (context-specific) β”‚ β€’ HttpOnly: true β”‚ β€’ Secure: true (production) β”‚ β€’ SameSite: Lax @@ -278,8 +382,9 @@ LOGOUT ## Key Takeaways -1. **Cookie Path Isolation** = No cross-context cookies -2. **Role Checking** = Admins blocked from vendor routes -3. **Dual Auth Support** = Cookies for pages, headers for API -4. **Security First** = HttpOnly, Secure, SameSite protection -5. **Clear Boundaries** = Each context is completely isolated +1. **Cookie Path Isolation** = Three separate cookies (admin_token, vendor_token, customer_token) with path-based isolation +2. **Role Checking** = Strict role validation at each boundary (admin, vendor, customer) +3. **Dual Auth Support** = Cookies for HTML pages, headers for API endpoints +4. **Security First** = HttpOnly, Secure, SameSite protection on all cookies +5. **Clear Boundaries** = Each context (admin/vendor/shop) is completely isolated +6. **Three User Types** = Admins manage platform, vendors manage stores, customers shop diff --git a/docs/architecture/diagrams/vendor-domain-diagrams.md b/docs/architecture/diagrams/vendor-domain-diagrams.md index 6d17e5da..40710e4a 100644 --- a/docs/architecture/diagrams/vendor-domain-diagrams.md +++ b/docs/architecture/diagrams/vendor-domain-diagrams.md @@ -106,7 +106,7 @@ β”‚ β”‚ 2. Pydantic Validation β”‚ β”‚ β”‚ β”‚ 3. Dependency Injection β”‚ β”‚ β”‚ β”‚ - get_db() β”‚ β”‚ -β”‚ β”‚ - get_current_admin_user() β”‚ β”‚ +β”‚ β”‚ - get_current_admin_api() β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ diff --git a/docs/backend/admin-feature-integration.md b/docs/backend/admin-feature-integration.md index 1b4f8831..646731c8 100644 --- a/docs/backend/admin-feature-integration.md +++ b/docs/backend/admin-feature-integration.md @@ -943,7 +943,7 @@ from typing import List from fastapi import APIRouter, Depends, Path, Body, Query from sqlalchemy.orm import Session -from app.api.deps import get_current_admin_user, get_db +from app.api.deps import get_current_admin_api, get_db from app.services.vendor_domain_service import vendor_domain_service from app.exceptions import VendorNotFoundException from models.schema.vendor_domain import ( @@ -992,7 +992,7 @@ def add_vendor_domain( vendor_id: int = Path(..., description="Vendor ID", gt=0), domain_data: VendorDomainCreate = Body(...), db: Session = Depends(get_db), - current_admin: User = Depends(get_current_admin_user), + current_admin: User = Depends(get_current_admin_api), ): """ Add a custom domain to vendor (Admin only). @@ -1040,7 +1040,7 @@ def add_vendor_domain( def list_vendor_domains( vendor_id: int = Path(..., description="Vendor ID", gt=0), db: Session = Depends(get_db), - current_admin: User = Depends(get_current_admin_user), + current_admin: User = Depends(get_current_admin_api), ): """ List all domains for a vendor (Admin only). @@ -1084,7 +1084,7 @@ def update_vendor_domain( domain_id: int = Path(..., description="Domain ID", gt=0), domain_update: VendorDomainUpdate = Body(...), db: Session = Depends(get_db), - current_admin: User = Depends(get_current_admin_user), + current_admin: User = Depends(get_current_admin_api), ): """ Update domain settings (Admin only). @@ -1127,7 +1127,7 @@ def update_vendor_domain( def delete_vendor_domain( domain_id: int = Path(..., description="Domain ID", gt=0), db: Session = Depends(get_db), - current_admin: User = Depends(get_current_admin_user), + current_admin: User = Depends(get_current_admin_api), ): """ Delete a custom domain (Admin only). @@ -1193,7 +1193,7 @@ router.include_router(users.router, tags=["admin-users"]) async def admin_vendor_domains_page( request: Request, vendor_code: str = Path(..., description="Vendor code"), - current_user: User = Depends(get_current_admin_user), + current_user: User = Depends(get_current_admin_from_cookie_or_header), db: Session = Depends(get_db) ): """ diff --git a/docs/backend/admin-integration-guide.md b/docs/backend/admin-integration-guide.md index a18d91e6..ec114fa4 100644 --- a/docs/backend/admin-integration-guide.md +++ b/docs/backend/admin-integration-guide.md @@ -145,10 +145,10 @@ Your API endpoints need to pass the current admin's ID to service methods: def create_vendor_with_owner( vendor_data: VendorCreate, db: Session = Depends(get_db), - current_admin: User = Depends(get_current_admin_user), + current_admin: User = Depends(get_current_admin_api), ): """Create vendor with audit logging.""" - + vendor, owner_user, temp_password = admin_service.create_vendor_with_owner( db=db, vendor_data=vendor_data, @@ -170,7 +170,7 @@ def create_vendor_with_owner( def toggle_vendor_status( vendor_id: int, db: Session = Depends(get_db), - current_admin: User = Depends(get_current_admin_user), + current_admin: User = Depends(get_current_admin_api), ): """Toggle vendor status with audit logging.""" vendor, message = admin_service.toggle_vendor_status( @@ -195,13 +195,13 @@ def delete_vendor( request: Request, # Add request parameter confirm: bool = Query(False), db: Session = Depends(get_db), - current_admin: User = Depends(get_current_admin_user), + current_admin: User = Depends(get_current_admin_api), ): """Delete vendor with full audit trail.""" - + if not confirm: raise HTTPException(status_code=400, detail="Confirmation required") - + # Get request metadata ip_address = request.client.host if request.client else None user_agent = request.headers.get("user-agent") diff --git a/docs/development/exception-handling.md b/docs/development/exception-handling.md index bad56856..dd82d327 100644 --- a/docs/development/exception-handling.md +++ b/docs/development/exception-handling.md @@ -195,7 +195,7 @@ Endpoints rely on service layer exceptions and don't handle HTTPException: def toggle_user_status( user_id: int, db: Session = Depends(get_db), - current_admin: User = Depends(get_current_admin_user), # May raise AdminRequiredException + current_admin: User = Depends(get_current_admin_api), # May raise AdminRequiredException ): # Service raises UserNotFoundException, CannotModifySelfException, etc. user, message = admin_service.toggle_user_status(db, user_id, current_admin.id) diff --git a/docs/frontend/admin/architecture.md b/docs/frontend/admin/architecture.md index ccc3b14e..ae035509 100644 --- a/docs/frontend/admin/architecture.md +++ b/docs/frontend/admin/architecture.md @@ -128,7 +128,7 @@ Example: @router.get("/admin/dashboard") async def admin_dashboard_page( request: Request, - current_user: User = Depends(get_current_admin_user) + current_user: User = Depends(get_current_admin_from_cookie_or_header) ): return templates.TemplateResponse( "admin/dashboard.html", @@ -364,10 +364,12 @@ Windmill Dashboard Theme: Auth Flow: 1. Login β†’ POST /api/v1/admin/auth/login 2. API β†’ Verify credentials + check admin role - 3. API β†’ Return JWT token - 4. JavaScript β†’ Store in localStorage - 5. API Client β†’ Add to all requests - 6. Routes β†’ Verify with get_current_admin_user + 3. API β†’ Return JWT token + set admin_token cookie + 4. JavaScript β†’ Store in localStorage (optional) + 5. HTML Pages β†’ Use cookie (automatic) + 6. API Calls β†’ Use Authorization header + 7. Routes β†’ Verify with get_current_admin_from_cookie_or_header (HTML) + or get_current_admin_api (API endpoints) Protected Routes: β€’ All /admin/* routes diff --git a/docs/frontend/admin/page-templates.md b/docs/frontend/admin/page-templates.md index 1c43c413..312ec852 100644 --- a/docs/frontend/admin/page-templates.md +++ b/docs/frontend/admin/page-templates.md @@ -862,7 +862,7 @@ pageLog.info('[Page Name] module loaded'); ```python from fastapi import APIRouter, Request, Depends -from app.core.auth import get_current_admin_user +from app.api.deps import get_current_admin_from_cookie_or_header from app.models.database.user import User router = APIRouter() @@ -870,12 +870,12 @@ router = APIRouter() @router.get("/admin/[page-route]") async def [page_name]_page( request: Request, - current_user: User = Depends(get_current_admin_user) + current_user: User = Depends(get_current_admin_from_cookie_or_header) ): """ [Page Name] page Displays [description] - + Requires admin authentication. """ return templates.TemplateResponse( diff --git a/docs/frontend/shared/sidebar.md b/docs/frontend/shared/sidebar.md index ea75d2fd..ce4f00a5 100644 --- a/docs/frontend/shared/sidebar.md +++ b/docs/frontend/shared/sidebar.md @@ -66,7 +66,7 @@ Update `app/api/v1/admin/pages.py` - add this route: @router.get("/icons", response_class=HTMLResponse, include_in_schema=False) async def admin_icons_page( request: Request, - current_user: User = Depends(get_current_admin_user), + current_user: User = Depends(get_current_admin_from_cookie_or_header), db: Session = Depends(get_db) ): """ diff --git a/docs/frontend/shared/ui-components.md b/docs/frontend/shared/ui-components.md index 05d884b1..829569ee 100644 --- a/docs/frontend/shared/ui-components.md +++ b/docs/frontend/shared/ui-components.md @@ -69,7 +69,7 @@ Update your `app/api/v1/admin/pages.py`: @router.get("/components", response_class=HTMLResponse, include_in_schema=False) async def admin_components_page( request: Request, - current_user: User = Depends(get_current_admin_user), + current_user: User = Depends(get_current_admin_from_cookie_or_header), db: Session = Depends(get_db) ): """