Open
Description
Description
Secure the API's mutating endpoints (POST
, PUT
, and DELETE
) by introducing JWT-based authentication following the OAuth 2.0 Client Credentials Flow.
In this model, trusted machine-to-machine clients can obtain a short-lived JWT by submitting their client_id
and client_secret
to a dedicated authentication endpoint. This token is then included in the Authorization
header to access protected resources.
sequenceDiagram
participant Client as Client (Machine-to-Machine app)
participant Server as Server (FastAPI RESTful API)
Note over Client,Server: Step 1 - Obtain Access Token
Client->>Server: POST /auth/token (client_id, client_secret)
Server-->>Client: 200 (OK) { access_token, expires_in, token_type }
Note over Client,Server: Step 2 - Use Token to Access Protected Resources
Client->>Server: POST /{resource}/{id} (Authorization: Bearer {access_token})
Server-->>Client: 201 (Created)
Client->>Server: PUT /{resource}/{id} (Authorization: Bearer {access_token})
Server-->>Client: 204 (No Content)
Client->>Server: DELETE /{resource}/{id} (Authorization: Bearer {access_token})
Server-->>Client: 204 (No Content)
This mechanism enhances API security by ensuring only authenticated clients can perform data mutations, while maintaining statelessness.
Proposed Solution
- Add an
/auth/token
route to issue JWTs to clients with valid credentials. - Introduce a simple in-memory (or configurable) client registry (
client_id
,client_secret
). - Secure mutating routes in
player_route.py
using a dependency that validates and decodes the incoming JWT. - Configure token expiration (e.g., 60 minutes) and signing via a secret key from environment/config.
- Include test coverage and update Postman collection for authentication.
Suggested Approach
1. Install dependencies
pip install python-jose[cryptography] python-dotenv
2. Create routes/auth_route.py
:
from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel
from jose import jwt
from datetime import datetime, timedelta
router = APIRouter(prefix="/auth", tags=["Auth"])
ALGORITHM = "HS256"
MINUTES = 60
# Ideally, load these from secure config or environment variables
CLIENT_ID = "foobarbaz"
CLIENT_SECRET = "#!_7h3qu1ck8r0wnf0xjump50v3r7h3l42yd09=@42"
KEY = "1LnBfWcu7gTDmqT41QCW4ANu1xsHMcseingKWruVveM="
class TokenModel(BaseModel):
grant_type: str
client_id: str
client_secret: str
@router.post("/token")
def get_token(data: TokenModel):
if data.grant_type != "client_credentials":
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Unsupported grant_type. Must be 'client_credentials'."
)
if data.client_id != CLIENT_ID or data.client_secret != CLIENT_SECRET:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid client credentials"
)
expire = datetime.utcnow() + timedelta(minutes=MINUTES)
payload = {
"sub": data.client_id,
"exp": expire,
"scope": "write"
}
token = jwt.encode(payload, KEY, algorithm=ALGORITHM)
return {
"access_token": token,
"token_type": "bearer",
"expires_in": MINUTES * 60
}
3. Create services/auth_dependency.py
:
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from jose import jwt, JWTError
import os
security = HTTPBearer()
ALGORITHM = "HS256"
# These values should be loaded from a secure configuration or environment variables
KEY = "1LnBfWcu7gTDmqT41QCW4ANu1xsHMcseingKWruVveM="
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
token = credentials.credentials
try:
payload = jwt.decode(token, KEY, algorithms=[ALGORITHM])
except JWTError:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid or expired token")
return payload
4. Secure mutating routes in routes/player_route.py
:
from services.auth_dependency import verify_token
@router.post("/", dependencies=[Depends(verify_token)])
def create_player(...): ...
5. Sample token request with curl
curl -X POST http://localhost:9000/auth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "foobarbaz",
"client_secret": "#!_7h3qu1ck8r0wnf0xjump50v3r7h3l42yd09_@42"
}'
Acceptance Criteria
/auth/token
endpoint issues valid JWT for known clientsPOST
,PUT
,DELETE
routes are secured and reject unauthenticated requests- Token expires after configured time; expired tokens are rejected
- Protected routes return
403 Forbidden
for invalid/missing JWTs - Configuration (e.g.,
JWT_SECRET
) is sourced from.env
or config - Unit tests cover both authentication and route protection
- Postman collection updated with new auth flow