Files
orion/docs/api/authentication-quick-reference.md
Samir Boulahtit cbfbbb4654 fix: customer authentication and shop error page styling
## Customer Authentication Fixes
- Fix get_current_customer_api to properly decode customer tokens (was using User model)
- Add _validate_customer_token() helper for shared customer token validation
- Add vendor validation: token.vendor_id must match request URL vendor
- Block admin/vendor tokens from shop endpoints (type != "customer")
- Update get_current_customer_optional to use proper customer token validation
- Customer auth functions now return Customer object (not User)

## Shop Orders API
- Update orders.py to receive Customer directly from auth dependency
- Remove broken get_customer_from_user() helper
- Use VendorNotFoundException instead of HTTPException

## Shop Error Pages
- Fix all error templates (400, 401, 403, 404, 422, 429, 500, 502, generic)
- Templates were using undefined CSS classes (.btn, .status-code, etc.)
- Now properly extend base.html and override specific blocks
- Use Tailwind utility classes for consistent styling

## Documentation
- Update docs/api/authentication.md with new Customer return types
- Document vendor validation security features
- Update docs/api/authentication-quick-reference.md examples

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 22:48:02 +01:00

274 lines
6.3 KiB
Markdown

# Authentication Quick Reference
**Version 1.0** | One-page reference for developers
---
## Function Cheat Sheet
### For HTML Pages (accept cookie OR header)
```python
from app.api.deps import (
get_current_admin_from_cookie_or_header,
get_current_vendor_from_cookie_or_header,
get_current_customer_from_cookie_or_header
)
from models.database.customer import Customer
# Admin page
@router.get("/admin/dashboard")
def admin_page(user: User = Depends(get_current_admin_from_cookie_or_header)):
pass
# Vendor page
@router.get("/vendor/{code}/dashboard")
def vendor_page(user: User = Depends(get_current_vendor_from_cookie_or_header)):
pass
# Customer page - NOTE: Returns Customer, not User!
@router.get("/shop/account/dashboard")
def customer_page(customer: Customer = Depends(get_current_customer_from_cookie_or_header)):
pass # customer.id, customer.email, customer.vendor_id
```
### For API Endpoints (header only - better security)
```python
from app.api.deps import (
get_current_admin_api,
get_current_vendor_api,
get_current_customer_api
)
from models.database.customer import Customer
# Admin API
@router.post("/api/v1/admin/vendors")
def admin_api(user: User = Depends(get_current_admin_api)):
pass
# Vendor API
@router.post("/api/v1/vendor/products")
def vendor_api(user: User = Depends(get_current_vendor_api)):
pass # user.token_vendor_id for vendor context
# Customer API - NOTE: Returns Customer, not User!
@router.post("/api/v1/shop/orders")
def customer_api(request: Request, customer: Customer = Depends(get_current_customer_api)):
pass # customer.id, request.state.vendor validated to match
```
---
## Three Authentication Contexts
| Context | Cookie | Path | Role | Routes |
|---------|--------|------|------|--------|
| **Admin** | `admin_token` | `/admin` | `admin` | `/admin/*` |
| **Vendor** | `vendor_token` | `/vendor` | `vendor` | `/vendor/*` |
| **Customer** | `customer_token` | `/shop` | `customer` | `/shop/account/*` |
---
## Access Control Matrix
| User | Admin Portal | Vendor Portal | Shop Catalog | Customer Account |
|------|--------------|---------------|--------------|------------------|
| Admin | ✅ | ❌ | ✅ (view) | ❌ |
| Vendor | ❌ | ✅ | ✅ (view) | ❌ |
| Customer | ❌ | ❌ | ✅ (view) | ✅ |
| Anonymous | ❌ | ❌ | ✅ (view) | ❌ |
---
## Login Endpoints
```bash
# Admin
POST /api/v1/admin/auth/login
Body: {"username": "...", "password": "..."}
# Vendor
POST /api/v1/vendor/auth/login
Body: {"username": "...", "password": "..."}
# Customer
POST /api/v1/public/vendors/{vendor_id}/customers/login
Body: {"username": "...", "password": "..."}
```
**Response:**
```json
{
"access_token": "eyJ0eXAi...",
"token_type": "Bearer",
"expires_in": 3600,
"user": {...}
}
```
Plus HTTP-only cookie is set automatically.
---
## Frontend Patterns
### Login (Store Token)
```javascript
const response = await fetch('/api/v1/admin/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const data = await response.json();
// Cookie set automatically
// Optionally store for API calls
localStorage.setItem('token', data.access_token);
// Navigate (cookie automatic)
window.location.href = '/admin/dashboard';
```
### API Call (Use Token)
```javascript
const token = localStorage.getItem('token');
const response = await fetch('/api/v1/admin/vendors', {
headers: {
'Authorization': `Bearer ${token}`
}
});
```
### Logout
```javascript
await fetch('/api/v1/admin/auth/logout', { method: 'POST' });
localStorage.removeItem('token');
window.location.href = '/admin/login';
```
---
## Testing Commands
### curl Examples
```bash
# Login
TOKEN=$(curl -X POST http://localhost:8000/api/v1/admin/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}' \
| jq -r '.access_token')
# Authenticated request
curl http://localhost:8000/api/v1/admin/vendors \
-H "Authorization: Bearer $TOKEN"
```
### Check Cookie in Browser
```javascript
// In DevTools console
document.cookie.split(';').forEach(c => console.log(c.trim()));
```
### Decode JWT
```javascript
function parseJwt(token) {
return JSON.parse(atob(token.split('.')[1]));
}
console.log(parseJwt(localStorage.getItem('token')));
```
---
## Common Errors
| Error | Meaning | Solution |
|-------|---------|----------|
| `INVALID_TOKEN` | No token or invalid | Re-login |
| `TOKEN_EXPIRED` | Token expired | Re-login |
| `ADMIN_REQUIRED` | Need admin role | Use correct account |
| `INSUFFICIENT_PERMISSIONS` | Wrong role for route | Use correct portal |
| `USER_NOT_ACTIVE` | Account disabled | Contact admin |
---
## Security Rules
1.**HTML pages** use `*_from_cookie_or_header` functions
2.**API endpoints** use `*_api` functions
3.**Admins** cannot access vendor/customer portals
4.**Vendors** cannot access admin/customer portals
5.**Customers** cannot access admin/vendor portals
6.**Public shop** (`/shop/products`) needs no auth
7.**Customer accounts** (`/shop/account/*`) need auth
---
## Cookie Security
All cookies have:
-`HttpOnly=true` - JavaScript cannot read (XSS protection)
-`Secure=true` - HTTPS only (production)
-`SameSite=Lax` - CSRF protection
- ✅ Path restriction - Context isolation
---
## Quick Debug
1. **Auth not working?**
- Check DevTools → Application → Cookies
- Verify cookie name and path match route
- Check token not expired
2. **Cross-context access denied?**
- This is intentional security
- Use correct portal for your role
3. **API call fails but page loads?**
- API needs `Authorization` header
- Page uses cookie (automatic)
- Add header to API calls
---
## File Locations
```
app/api/
├── deps.py # All auth functions here
├── v1/
├── admin/auth.py # Admin login
├── vendor/auth.py # Vendor login
└── public/vendors/auth.py # Customer login
```
---
## Environment Variables
```bash
JWT_SECRET_KEY=your-secret-key
JWT_ALGORITHM=HS256
JWT_EXPIRATION=3600 # 1 hour
ENVIRONMENT=production
```
---
**Full Documentation:** See [Authentication System Documentation](authentication.md)
**Questions?** Contact backend team
---
**Print this page for quick reference!**