Authentication and Security
Relevant source files
The following files were used as context for generating this wiki page:The authentication and security system for the QA Dashboard is built around JSON Web Tokens (JWT) for stateless session management and passlib for secure password hashing. It provides a clear separation of concerns, with dedicated modules for security logic, API endpoints, and data models. The system supports user registration, login, and role-based access control, distinguishing between regular users and administrators.
The core of the system is implemented in the FastAPI backend. It exposes RESTful endpoints for user registration and login, which the Next.js frontend consumes. Once authenticated, the client receives a JWT Bearer token that must be included in the Authorization header for all subsequent requests to protected endpoints. The system also includes a mechanism for handling anonymous or guest users.
Core Components
The authentication system is primarily managed by a few key files in the backend.
| File | Description |
|---|---|
backend/security.py | Contains core security functions for password hashing/verification, JWT creation/decoding, and FastAPI dependencies for user retrieval. |
backend/api/auth.py | Defines the API routes for authentication, such as /register and /login. |
backend/database.py | Defines the User SQLAlchemy model, which represents the users table in the database. |
backend/schemas.py | Defines the Pydantic models (UserCreate, UserLogin, Token, UserResponse) used for data validation and serialization in API requests/responses. |
backend/settings.py | Manages configuration, including the JWT secret key, algorithm, and token expiration time, loaded from environment variables. |
Sources: backend/security.py, backend/api/auth.py, backend/database.py, backend/schemas.py, backend/settings.py
Password Management
Password security is handled using passlib. All user passwords are required to be hashed before being stored in the database.
Sources: backend/security.py:8-19
Hashing and Verification
- Hashing Algorithm: The system uses
argon2as the hashing scheme, configured viaCryptContext. - Hashing Function: The
get_password_hash(password: str)function takes a plain-text password and returns itsargon2hash. - Verification Function: The
verify_password(plain_password: str, hashed_password: str)function compares a plain-text password against a stored hash, returningTrueif they match.
# backend/security.py:8-19
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Verify a plain password against a hashed password"""
print("Plain password: ", plain_password)
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
"""Hash a password"""
print("Password before hash: ", password)
hashed_result = pwd_context.hash(password)
print("Hashed password: ", hashed_result)
return hashed_result
Sources: backend/security.py:8-19
JWT Token Handling
The application uses JWT for stateless authentication. A token is generated upon successful login and is decoded on subsequent requests to identify the user.
Token Creation
The create_access_token function generates a JWT. The token's payload contains the user's ID as the subject (sub) and an expiration timestamp (exp).
Key elements:
- Payload: A dictionary containing data to be encoded. The user's ID is passed under the
subkey. - Expiration: The token's lifetime is set by
JWT_EXPIRATION_HOURSfrom the settings, defaulting to 24 hours. - Signing: The token is signed using the
SECRET_KEYandJWT_ALGORITHMspecified in the application settings.
Sources: backend/security.py:22-33, backend/settings.py
Token Decoding and Verification
The decode_token function validates and decodes an incoming JWT. It uses the same secret key and algorithm to verify the token's signature. If the token is invalid or expired, it returns None.
There is a special case for an anonymous user, where a hardcoded token string f"{settings.ANONYMOUS_USER_ID}-anonymous-token" is checked, and if it matches, a payload with the anonymous user's ID is returned.
Sources: backend/security.py:36-50
Configuration
JWT behavior is configured via environment variables, which are loaded into the settings object.
| Variable | Description | Default Value | Source File |
|---|---|---|---|
SECRET_KEY | The secret key for signing JWTs. | your-secret-key-change-in-production | README.md, backend/settings.py |
JWT_ALGORITHM | The algorithm used for signing. | HS256 | README.md, backend/settings.py |
JWT_EXPIRATION_HOURS | The token's validity period in hours. | 24 | README.md, backend/settings.py |
ANONYMOUS_USER_ID | A hardcoded UUID for the anonymous user. | Not specified | backend/settings.py |
Sources: README.md, backend/settings.py
Authentication Flows and API Endpoints
The authentication process is handled by endpoints defined in backend/api/auth.py.
API Endpoints
| Method | Endpoint | Description | Request Body | Response Body |
|---|---|---|---|---|
POST | /auth/register | Registers a new user. Checks for existing username/email. Hashes password. | UserCreate | UserResponse |
POST | /auth/login | Authenticates a user and returns a JWT. | UserLogin | Token |
GET | /auth/me | Returns information for the currently authenticated user. Requires a valid JWT. | N/A | UserResponse |
Sources: backend/api/auth.py, backend/schemas.py
User Registration Flow
The diagram below illustrates the sequence of events when a new user registers.
This diagram shows the user registration process, from the client request to database insertion.
Sources: backend/api/auth.py:14-36, web/src/app/register/page.js
User Login Flow
This diagram shows the sequence for a user logging in and receiving a JWT.
This diagram illustrates the user login process, including password verification and JWT generation.
Sources: backend/api/auth.py:39-52, web/src/app/login/page.js
User and Role Management
User identity and permissions are managed through the User model and FastAPI dependencies.
User Model
The User model defines the schema for the users table. It includes an is_admin boolean flag to differentiate between regular users and administrators.
This ER diagram shows the structure of the users table.
Sources: backend/database.py:31-43
User Retrieval and Authorization
The get_current_user dependency is used on protected endpoints to resolve a user from an Authorization header.
- It expects an
HTTPAuthorizationCredentialsobject (Bearer token). - If no credentials are provided, it returns
None. - It calls
decode_tokento validate the JWT. - If the token is invalid, it raises a
401 UnauthorizedHTTPException. - It extracts the user ID (
sub) from the payload. - It handles the special case for the anonymous user ID.
- For regular users, it queries the database for the user by ID. If not found, it raises a
401 Unauthorizederror. - Finally, it returns the
Userobject.
Sources: backend/security.py:53-85
Admin-Only Access
For endpoints that require administrator privileges, the get_admin_user dependency is used. It builds upon get_current_user and adds a check to ensure the resolved user has is_admin set to True. If not, it raises a 403 Forbidden HTTPException.
# backend/security.py:88-94
async def get_admin_user(current_user: User = Depends(get_current_user)) -> User:
"""Ensure user is admin"""
if not current_user.is_admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required"
)
return current_user
Sources: backend/security.py:88-94
Summary
The QA Dashboard employs a robust and standard security architecture for a modern web application. It leverages stateless JWTs for authentication, strong argon2 hashing for password storage, and a clear role-based access control system via an is_admin flag. The implementation is modular, with distinct components for handling security logic, API routing, and data modeling, making it maintainable and secure. The system correctly handles user registration, login, and token-based authorization for protected resources.