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
```
┌─────────────────────────────────────────────────────────────┐
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 <token>) │
┌────────────────────────────┐
│ 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 <token>) │
┌────────────────────────────┐
│ 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=<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
```
┌─────────────────────┐
│ 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=<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
```
@@ -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

View File

@@ -106,7 +106,7 @@
│ │ 2. Pydantic Validation │ │
│ │ 3. Dependency Injection │ │
│ │ - 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 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)
):
"""

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(
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")

View File

@@ -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)

View File

@@ -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

View File

@@ -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(

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)
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)
):
"""

View File

@@ -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)
):
"""