Fixed middleware authentication issues
This commit is contained in:
@@ -434,6 +434,8 @@ current_user: User = Depends(get_current_admin_api)
|
||||
- `InvalidTokenException` - No token or invalid token
|
||||
- `InsufficientPermissionsException` - User is not vendor or is admin
|
||||
|
||||
**Note:** The `InsufficientPermissionsException` raised here is from `app.exceptions.auth`, which provides general authentication permission checking. This is distinct from `InsufficientTeamPermissionsException` used for team-specific permissions.
|
||||
|
||||
**Usage:**
|
||||
```python
|
||||
current_user: User = Depends(get_current_vendor_from_cookie_or_header)
|
||||
@@ -462,6 +464,8 @@ current_user: User = Depends(get_current_vendor_api)
|
||||
- `InvalidTokenException` - No token or invalid token
|
||||
- `InsufficientPermissionsException` - User is not customer (admin/vendor blocked)
|
||||
|
||||
**Note:** The `InsufficientPermissionsException` raised here is from `app.exceptions.auth`, which provides general authentication permission checking. This is distinct from `InsufficientTeamPermissionsException` used for team-specific permissions.
|
||||
|
||||
**Usage:**
|
||||
```python
|
||||
current_customer: Customer = Depends(get_current_customer_from_cookie_or_header)
|
||||
@@ -563,6 +567,40 @@ Tokens are validated on every request:
|
||||
5. Verify user is active
|
||||
6. Check role matches route requirements
|
||||
|
||||
#### Token Validation Edge Cases
|
||||
|
||||
The token verification process includes comprehensive validation of token claims:
|
||||
|
||||
**Required Claims Validation:**
|
||||
- **Missing `sub` (User ID)**: Raises `InvalidTokenException("Token missing user identifier")`
|
||||
- **Missing `exp` (Expiration)**: Raises `InvalidTokenException("Token missing expiration")`
|
||||
- **Expired Token**: Raises `TokenExpiredException()`
|
||||
|
||||
**Signature Verification:**
|
||||
- **Invalid Signature**: Raises `InvalidTokenException("Could not validate credentials")`
|
||||
- **Wrong Algorithm**: Raises `InvalidTokenException()`
|
||||
- **Malformed Token**: Raises `InvalidTokenException()`
|
||||
|
||||
**Exception Handling Pattern:**
|
||||
Custom exceptions (such as those raised for missing claims) are preserved with their specific error messages, allowing for detailed error reporting to clients. This follows the exception handling pattern documented in the [Exception Handling Guide](../development/exception-handling.md).
|
||||
|
||||
**Example Error Responses:**
|
||||
```json
|
||||
{
|
||||
"error_code": "INVALID_TOKEN",
|
||||
"message": "Token missing user identifier",
|
||||
"status_code": 401
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"error_code": "TOKEN_EXPIRED",
|
||||
"message": "Token has expired",
|
||||
"status_code": 401
|
||||
}
|
||||
```
|
||||
|
||||
### HTTPS Requirement
|
||||
|
||||
**Production Environment:**
|
||||
|
||||
@@ -193,17 +193,121 @@ async def customer_orders(
|
||||
|
||||
### require_role()
|
||||
|
||||
Custom role enforcement for specific roles.
|
||||
Custom role enforcement for specific roles. This method returns a decorator factory that creates role-checking decorators for any role name.
|
||||
|
||||
**Usage**:
|
||||
**Method Signature**:
|
||||
```python
|
||||
@app.get("/custom-endpoint")
|
||||
@auth_manager.require_role("custom_role")
|
||||
async def custom_endpoint(current_user: User):
|
||||
return {"message": "Custom role access"}
|
||||
def require_role(self, required_role: str) -> Callable
|
||||
```
|
||||
|
||||
**Returns**: Decorator function that validates role
|
||||
**Parameters**:
|
||||
- `required_role` (str): The exact role name required (e.g., "admin", "vendor", "custom_role")
|
||||
|
||||
**Returns**: A decorator function that:
|
||||
1. Accepts a function as input
|
||||
2. Returns a wrapper that validates the current user's role
|
||||
3. Raises `HTTPException(403)` if the role doesn't match
|
||||
|
||||
**Usage Example**:
|
||||
```python
|
||||
from fastapi import Depends, APIRouter
|
||||
from middleware.auth import auth_manager
|
||||
from models.database.user import User
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/moderator-only")
|
||||
@auth_manager.require_role("moderator")
|
||||
async def moderator_endpoint(current_user: User):
|
||||
"""Only users with role='moderator' can access this."""
|
||||
return {"message": "Moderator access granted"}
|
||||
|
||||
# Can also be used with custom roles
|
||||
@router.get("/special-access")
|
||||
@auth_manager.require_role("special_user")
|
||||
async def special_endpoint(current_user: User):
|
||||
return {"data": "special content"}
|
||||
```
|
||||
|
||||
**Error Response**:
|
||||
```json
|
||||
{
|
||||
"detail": "Required role 'moderator' not found. Current role: 'vendor'"
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: For standard roles (admin, vendor, customer), prefer using the dedicated methods (`require_admin()`, `require_vendor()`, `require_customer()`) as they provide better error handling and custom exceptions.
|
||||
|
||||
### create_default_admin_user()
|
||||
|
||||
Creates a default admin user if one doesn't already exist. This is typically used during initial application setup or database seeding.
|
||||
|
||||
**Method Signature**:
|
||||
```python
|
||||
def create_default_admin_user(self, db: Session) -> User
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `db` (Session): SQLAlchemy database session
|
||||
|
||||
**Returns**: `User` object (either the existing admin user or the newly created one)
|
||||
|
||||
**Behavior**:
|
||||
1. Checks if a user with username "admin" already exists
|
||||
2. If not found, creates a new admin user with:
|
||||
- Username: `admin`
|
||||
- Email: `admin@example.com`
|
||||
- Password: `admin123` (hashed with bcrypt)
|
||||
- Role: `admin`
|
||||
- Status: Active
|
||||
3. If found, returns the existing user without modification
|
||||
|
||||
**Usage Example**:
|
||||
```python
|
||||
from app.core.database import SessionLocal
|
||||
from middleware.auth import auth_manager
|
||||
|
||||
# During application startup or database initialization
|
||||
db = SessionLocal()
|
||||
try:
|
||||
admin_user = auth_manager.create_default_admin_user(db)
|
||||
print(f"Admin user ready: {admin_user.username}")
|
||||
finally:
|
||||
db.close()
|
||||
```
|
||||
|
||||
**Security Warning**:
|
||||
⚠️ The default credentials (`admin` / `admin123`) should be changed immediately after first login in production environments. Consider using environment variables for initial admin credentials:
|
||||
|
||||
```python
|
||||
# Example: Custom admin creation with env variables
|
||||
import os
|
||||
|
||||
def create_admin_from_env(db: Session):
|
||||
admin_username = os.getenv("ADMIN_USERNAME", "admin")
|
||||
admin_password = os.getenv("ADMIN_PASSWORD", "admin123")
|
||||
admin_email = os.getenv("ADMIN_EMAIL", "admin@example.com")
|
||||
|
||||
# Check if admin exists
|
||||
admin = db.query(User).filter(User.username == admin_username).first()
|
||||
if not admin:
|
||||
admin = User(
|
||||
username=admin_username,
|
||||
email=admin_email,
|
||||
hashed_password=auth_manager.hash_password(admin_password),
|
||||
role="admin",
|
||||
is_active=True
|
||||
)
|
||||
db.add(admin)
|
||||
db.commit()
|
||||
return admin
|
||||
```
|
||||
|
||||
**Typical Use Cases**:
|
||||
- Initial database setup scripts
|
||||
- Application bootstrap/initialization
|
||||
- Development environment setup
|
||||
- Testing fixtures
|
||||
|
||||
## JWT Token Structure
|
||||
|
||||
|
||||
Reference in New Issue
Block a user