docs: update authentication dependency names across documentation

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 <noreply@anthropic.com>
This commit is contained in:
2025-11-22 18:21:23 +01:00
parent 7a9c12dcdf
commit 0d7915c275
9 changed files with 213 additions and 106 deletions

View File

@@ -3,30 +3,33 @@
## Cookie Isolation Architecture ## Cookie Isolation Architecture
``` ```
┌─────────────────────────────────────────────────────────────┐ ┌──────────────────────────────────────────────────────────────────────────────
Browser Browser
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │ │ ┌────────────────── ┌──────────────────┐ ┌──────────────────┐
│ │ Admin Area │ Vendor Area │ │ │ │ Admin Area │ Vendor Area │ │ Shop Area
│ │ /admin/* │ /vendor/* │ │ │ │ /admin/* │ /vendor/* │ │ /shop/*
│ │ │ │ │ │ │ │ │ │
│ │ 🍪 admin_token │ 🍪 vendor_token │ │ │ │ 🍪 admin_token │ 🍪 vendor_token │ │ 🍪 customer_
│ │ Path: /admin │ Path: /vendor │ │ │ │ Path: /admin │ Path: /vendor │ │ token
└─────────────────────┘ └─────────────────────┘ │ │ │ │ │ Path: /shop │
│ │ └──────────────────┘ └──────────────────┘ └──────────────────┘
├───────────────────────────┤ │ │ │
│ ❌ No Cookie Mixing │ ├──────────────────────┼─────────────────────┤
│ │ │ │ ❌ No Cookie Mixing │ │
└───────────┼───────────────────────────┼──────────────────────┘ │ │ │ │ │
│ │ └───────────┼──────────────────────┼─────────────────────┼─────────────────────┘
│ │
┌───────────────────────┐ ┌───────────────────────┐ ▼ ▼ ▼
│ Admin Backend │ │ Vendor Backend │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
/admin/* /vendor/* Admin Backend │ │ Vendor Backend Shop Backend
/admin/* │ │ /vendor/* /shop/*
✅ admin_token │ │ ✅ vendor_token │ │ │ │
❌ vendor_token ❌ admin_token ✅ admin_token │ │ ✅ vendor_token │ │ ✅ customer_
└───────────────────────┘ └───────────────────────┘ │ ❌ vendor_token │ │ ❌ admin_token │ │ token │
│ ❌ customer_ │ │ ❌ customer_ │ │ ❌ admin_token │
│ token │ │ token │ │ ❌ vendor_token │
└──────────────────┘ └──────────────────┘ └──────────────────┘
``` ```
## Login Flow - Admin ## Login Flow - Admin
@@ -63,15 +66,17 @@
└── API call to /api/v1/admin/vendors ───────┤ └── API call to /api/v1/admin/vendors ───────┤
(Authorization: Bearer <token>) │ (Authorization: Bearer <token>) │
┌────────────────────────────┐ ┌────────────────────────────────────────┐
│ get_current_admin_user() HTML: get_current_admin_from_
cookie_or_header()
1. Check Auth header API: get_current_admin_api()
2. Check admin_token cookie
3. Validate JWT 1. Check Auth header
4. Verify role == admin 2. Check admin_token cookie (HTML)
✅ Return User 3. Validate JWT
└──────────────────────────────┘ │ 4. Verify role == admin │
│ ✅ Return User │
└────────────────────────────────────┘
``` ```
## Login Flow - Vendor ## Login Flow - Vendor
@@ -109,54 +114,104 @@
└── API call to /api/v1/vendor/ACME/products ┤ └── API call to /api/v1/vendor/ACME/products ┤
(Authorization: Bearer <token>) │ (Authorization: Bearer <token>) │
┌────────────────────────────┐ ┌────────────────────────────────────────┐
│ get_current_vendor_user() HTML: get_current_vendor_from_
cookie_or_header()
1. Check Auth header API: get_current_vendor_api()
2. Check vendor_token cookie
3. Validate JWT 1. Check Auth header
│ 2. Check vendor_token cookie (HTML)│
│ 3. Validate JWT │
│ 4. Block if admin │──> ❌ Error │ 4. Block if admin │──> ❌ Error
│ 5. Verify vendor access │ │ 5. Verify vendor access
│ ✅ Return User │ │ ✅ 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=<JWT>; 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 <token>) │
┌────────────────────────────────────────┐
│ 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 ## Security Boundary Enforcement
``` ```
┌─────────────────────┐ ┌─────────────────────┐
│ Request Comes In │ │ Request Comes In │
└──────────┬──────────┘ └──────────┬──────────┘
┌──────────▼──────────┐ ┌──────────▼──────────┐
│ What's the path? │ │ What's the path? │
└──────────┬──────────┘ └──────────┬──────────┘
──────────────────────────────┐ ┌───────────────────────────┼───────────────────────────┐
│ │ │ │ │
Starts with Starts with Starts with Starts with Starts with Starts with Starts with Starts with
/admin/* /vendor/* /api/* /admin/* /vendor/* /shop/* /api/* (public)
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────┐┌────────────────┐┌────────────────┐┌────────────────┐┌────────────────┐
│ Check for: │ Check for: │ Check for: │ │ Check for: ││ Check for: │ Check for: ││ Check for: ││ No Auth
│ - admin_token │ - vendor_token │ │ - Authorization │ │ - admin_token │ - vendor_token ││ - customer_ ││ - Authorization││ Required
│ cookie │ cookie │ │ header │ cookie │ cookie ││ token cookie ││ header ││
│ - OR Auth header │ - OR Auth header │ (required) │ │ - OR Auth │ - OR Auth ││ - OR Auth ││ (required) ││ Public pages
└────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘ │ header ││ header ││ header ││ ││ & assets │
│ │ │ └────────┬───────┘└────────┬───────┘└────────┬───────┘└────────┬───────┘└────────────────┘
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ▼ ▼ ▼ ▼
│ Validate: │ │ Validate: │ │ Validate: │ ┌────────────────┐┌────────────────┐┌────────────────┐┌────────────────┐
│ - JWT valid - JWT valid - JWT valid │ Validate: ││ Validate: ││ Validate: ││ Validate:
│ - User active │ - User active │ - User active │ - JWT valid - JWT valid ││ - JWT valid ││ - JWT valid
│ - Role = admin │ - Role != admin │ - Any role │ - User active - User active ││ - User active ││ - User active
│ - Has vendor │ │ (depends on │ - Role = admin ││ - Role != admin││ - Role = │ - Any role
access endpoint) ││ - Has vendor ││ customer (depends on
└────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘ ││ access ││ ││ endpoint) │
│ │ │ └────────┬───────┘└────────┬───────┘└────────┬───────┘└────────┬───────┘
✅ Allowed ✅ Allowed ✅ Allowed ▼ ▼
✅ Allowed ✅ Allowed ✅ Allowed ✅ Allowed
``` ```
## Cross-Context Prevention ## Cross-Context Prevention
@@ -229,6 +284,55 @@ Admin cookie sent to vendor route:
"Vendor authentication required" "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=<JWT> (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 ## Cookie Lifecycle
``` ```
@@ -236,9 +340,9 @@ LOGIN
├── Server generates JWT ├── Server generates JWT
├── Server sets cookie: ├── Server sets cookie:
│ • Name: admin_token or vendor_token │ • Name: admin_token, vendor_token, or customer_token
│ • Value: JWT │ • Value: JWT
│ • Path: /admin or /vendor │ • Path: /admin, /vendor, or /shop (context-specific)
│ • HttpOnly: true │ • HttpOnly: true
│ • Secure: true (production) │ • Secure: true (production)
│ • SameSite: Lax │ • SameSite: Lax
@@ -278,8 +382,9 @@ LOGOUT
## Key Takeaways ## Key Takeaways
1. **Cookie Path Isolation** = No cross-context cookies 1. **Cookie Path Isolation** = Three separate cookies (admin_token, vendor_token, customer_token) with path-based isolation
2. **Role Checking** = Admins blocked from vendor routes 2. **Role Checking** = Strict role validation at each boundary (admin, vendor, customer)
3. **Dual Auth Support** = Cookies for pages, headers for API 3. **Dual Auth Support** = Cookies for HTML pages, headers for API endpoints
4. **Security First** = HttpOnly, Secure, SameSite protection 4. **Security First** = HttpOnly, Secure, SameSite protection on all cookies
5. **Clear Boundaries** = Each context is completely isolated 5. **Clear Boundaries** = Each context (admin/vendor/shop) is completely isolated
6. **Three User Types** = Admins manage platform, vendors manage stores, customers shop

View File

@@ -106,7 +106,7 @@
│ │ 2. Pydantic Validation │ │ │ │ 2. Pydantic Validation │ │
│ │ 3. Dependency Injection │ │ │ │ 3. Dependency Injection │ │
│ │ - get_db() │ │ │ │ - get_db() │ │
│ │ - get_current_admin_user() │ │ │ │ - get_current_admin_api() │ │
│ └──────────────────────────────────────┘ │ │ └──────────────────────────────────────┘ │
└────────────────┬───────────────────────────┘ └────────────────┬───────────────────────────┘

View File

@@ -943,7 +943,7 @@ from typing import List
from fastapi import APIRouter, Depends, Path, Body, Query from fastapi import APIRouter, Depends, Path, Body, Query
from sqlalchemy.orm import Session 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.services.vendor_domain_service import vendor_domain_service
from app.exceptions import VendorNotFoundException from app.exceptions import VendorNotFoundException
from models.schema.vendor_domain import ( from models.schema.vendor_domain import (
@@ -992,7 +992,7 @@ def add_vendor_domain(
vendor_id: int = Path(..., description="Vendor ID", gt=0), vendor_id: int = Path(..., description="Vendor ID", gt=0),
domain_data: VendorDomainCreate = Body(...), domain_data: VendorDomainCreate = Body(...),
db: Session = Depends(get_db), 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). Add a custom domain to vendor (Admin only).
@@ -1040,7 +1040,7 @@ def add_vendor_domain(
def list_vendor_domains( def list_vendor_domains(
vendor_id: int = Path(..., description="Vendor ID", gt=0), vendor_id: int = Path(..., description="Vendor ID", gt=0),
db: Session = Depends(get_db), 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). 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_id: int = Path(..., description="Domain ID", gt=0),
domain_update: VendorDomainUpdate = Body(...), domain_update: VendorDomainUpdate = Body(...),
db: Session = Depends(get_db), 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). Update domain settings (Admin only).
@@ -1127,7 +1127,7 @@ def update_vendor_domain(
def delete_vendor_domain( def delete_vendor_domain(
domain_id: int = Path(..., description="Domain ID", gt=0), domain_id: int = Path(..., description="Domain ID", gt=0),
db: Session = Depends(get_db), 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). Delete a custom domain (Admin only).
@@ -1193,7 +1193,7 @@ router.include_router(users.router, tags=["admin-users"])
async def admin_vendor_domains_page( async def admin_vendor_domains_page(
request: Request, request: Request,
vendor_code: str = Path(..., description="Vendor code"), 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) db: Session = Depends(get_db)
): ):
""" """

View File

@@ -145,10 +145,10 @@ Your API endpoints need to pass the current admin's ID to service methods:
def create_vendor_with_owner( def create_vendor_with_owner(
vendor_data: VendorCreate, vendor_data: VendorCreate,
db: Session = Depends(get_db), 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.""" """Create vendor with audit logging."""
vendor, owner_user, temp_password = admin_service.create_vendor_with_owner( vendor, owner_user, temp_password = admin_service.create_vendor_with_owner(
db=db, db=db,
vendor_data=vendor_data, vendor_data=vendor_data,
@@ -170,7 +170,7 @@ def create_vendor_with_owner(
def toggle_vendor_status( def toggle_vendor_status(
vendor_id: int, vendor_id: int,
db: Session = Depends(get_db), 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.""" """Toggle vendor status with audit logging."""
vendor, message = admin_service.toggle_vendor_status( vendor, message = admin_service.toggle_vendor_status(
@@ -195,13 +195,13 @@ def delete_vendor(
request: Request, # Add request parameter request: Request, # Add request parameter
confirm: bool = Query(False), confirm: bool = Query(False),
db: Session = Depends(get_db), 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.""" """Delete vendor with full audit trail."""
if not confirm: if not confirm:
raise HTTPException(status_code=400, detail="Confirmation required") raise HTTPException(status_code=400, detail="Confirmation required")
# Get request metadata # Get request metadata
ip_address = request.client.host if request.client else None ip_address = request.client.host if request.client else None
user_agent = request.headers.get("user-agent") user_agent = request.headers.get("user-agent")

View File

@@ -195,7 +195,7 @@ Endpoints rely on service layer exceptions and don't handle HTTPException:
def toggle_user_status( def toggle_user_status(
user_id: int, user_id: int,
db: Session = Depends(get_db), 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. # Service raises UserNotFoundException, CannotModifySelfException, etc.
user, message = admin_service.toggle_user_status(db, user_id, current_admin.id) user, message = admin_service.toggle_user_status(db, user_id, current_admin.id)

View File

@@ -128,7 +128,7 @@ Example:
@router.get("/admin/dashboard") @router.get("/admin/dashboard")
async def admin_dashboard_page( async def admin_dashboard_page(
request: Request, request: Request,
current_user: User = Depends(get_current_admin_user) current_user: User = Depends(get_current_admin_from_cookie_or_header)
): ):
return templates.TemplateResponse( return templates.TemplateResponse(
"admin/dashboard.html", "admin/dashboard.html",
@@ -364,10 +364,12 @@ Windmill Dashboard Theme:
Auth Flow: Auth Flow:
1. Login → POST /api/v1/admin/auth/login 1. Login → POST /api/v1/admin/auth/login
2. API → Verify credentials + check admin role 2. API → Verify credentials + check admin role
3. API → Return JWT token 3. API → Return JWT token + set admin_token cookie
4. JavaScript → Store in localStorage 4. JavaScript → Store in localStorage (optional)
5. API Client → Add to all requests 5. HTML Pages → Use cookie (automatic)
6. Routes → Verify with get_current_admin_user 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: Protected Routes:
• All /admin/* routes • All /admin/* routes

View File

@@ -862,7 +862,7 @@ pageLog.info('[Page Name] module loaded');
```python ```python
from fastapi import APIRouter, Request, Depends 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 from app.models.database.user import User
router = APIRouter() router = APIRouter()
@@ -870,12 +870,12 @@ router = APIRouter()
@router.get("/admin/[page-route]") @router.get("/admin/[page-route]")
async def [page_name]_page( async def [page_name]_page(
request: Request, request: Request,
current_user: User = Depends(get_current_admin_user) current_user: User = Depends(get_current_admin_from_cookie_or_header)
): ):
""" """
[Page Name] page [Page Name] page
Displays [description] Displays [description]
Requires admin authentication. Requires admin authentication.
""" """
return templates.TemplateResponse( return templates.TemplateResponse(

View File

@@ -66,7 +66,7 @@ Update `app/api/v1/admin/pages.py` - add this route:
@router.get("/icons", response_class=HTMLResponse, include_in_schema=False) @router.get("/icons", response_class=HTMLResponse, include_in_schema=False)
async def admin_icons_page( async def admin_icons_page(
request: Request, 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) db: Session = Depends(get_db)
): ):
""" """

View File

@@ -69,7 +69,7 @@ Update your `app/api/v1/admin/pages.py`:
@router.get("/components", response_class=HTMLResponse, include_in_schema=False) @router.get("/components", response_class=HTMLResponse, include_in_schema=False)
async def admin_components_page( async def admin_components_page(
request: Request, 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) db: Session = Depends(get_db)
): ):
""" """