Authentication & Authorization (JWT)
Relevant source files
The following files were used as context for generating this wiki page:
The authentication and authorization system for the Email Platform is built upon JSON Web Tokens (JWT). It is implemented in the API service using Fastify and the @fastify/jwt plugin. This system is responsible for managing user identity, securing API endpoints, and controlling access based on user roles.
The core functionalities include user registration with optional email verification, a secure login process with support for two-factor authentication (2FA), password management, and role-based access control (RBAC). The frontend React application interacts with this system via a dedicated AuthContext to manage user sessions and authenticated requests.
Core Components & Architecture
The system relies on several key libraries and concepts to ensure security and functionality.
@fastify/jwt: This Fastify plugin is central to the authentication mechanism. It handles the signing and verification of JWTs and provides decorators likeapp.jwt.sign()andapp.authenticateto protect routes.bcryptjs: Used for securely hashing and verifying user passwords. The implementation is abstracted inservices/api/src/utils/password.ts.otplib: This library is used for generating and verifying Time-based One-Time Passwords (TOTP) for the 2FA feature.crypto: The native Node.js crypto module is used for generating secure random tokens (e.g., for email verification) and for encrypting the 2FA secret stored in the database.- Prisma & PostgreSQL: The database stores user credentials, including the hashed password, 2FA secrets, and user role.
JWT Payload
When a user successfully authenticates, a JWT is generated with a specific payload. This payload is attached to the request.user object on protected routes.
| Field | Type | Description | Source |
|---|---|---|---|
userId | string | The unique identifier (UUID) of the user. | services/api/src/routes/auth.ts:77 |
role | string | The user's role (e.g., USER, ADMIN). | services/api/src/routes/auth.ts:77 |
pending2FA | boolean | (Temporary token only) Indicates that the user must complete 2FA. | services/api/src/routes/auth.ts:241 |
iat | number | Issued at timestamp (standard JWT claim). | @fastify/jwt |
exp | number | Expiration timestamp (standard JWT claim). | @fastify/jwt |
The structure of the request.user object is defined for TypeScript using declaration merging.
Sources: services/api/src/types/fastify-jwt.d.ts
import "@fastify/jwt";
declare module "@fastify/jwt" {
interface FastifyJWT {
payload: {
userId: string;
role: "USER" | "ADMIN";
pending2FA?: boolean;
};
user: {
userId: string;
role: "USER" | "ADMIN";
pending2FA?: boolean;
};
}
}
Password & Secret Management
Passwords are never stored in plaintext. They are hashed using bcryptjs with a salt round of 10. The verifyPassword function compares a provided password against the stored hash.
Sources: services/api/src/utils/password.ts:3-11
import bcrypt from "bcryptjs";
const SALT_ROUNDS = 10;
export const hashPassword = (password: string) => {
return bcrypt.hash(password, SALT_ROUNDS);
};
export const verifyPassword = (password: string, hash: string) => {
return bcrypt.compare(password, hash);
};
Similarly, the 2FA secret is encrypted before being stored in the database using AES-256-CBC to prevent exposure.
Sources: services/api/src/utils/encryption.ts, services/api/src/routes/auth.ts:401-405
User Registration & Verification
The registration process creates a new user account. It can be configured to require email verification before the user can log in.
This diagram illustrates the user registration flow.
Key Endpoints
POST /auth/register: Creates a new user. IfappConfig.requireEmailVerificationis true, it generates averificationTokenand sends a verification email.POST /auth/verify-email: Consumes theverificationTokensent to the user's email. If the token is valid and not expired, it marks the user's email as verified.POST /auth/resend-verification: Allows an unverified user to request a new verification email.
Sources: services/api/src/routes/auth.ts:11-188
User Login
The login process validates user credentials and issues a JWT. It includes checks for email verification, account status, and two-factor authentication.
This diagram shows the complete login flow, including the 2FA path.
Two-Factor Authentication (2FA)
The system supports TOTP-based 2FA for enhanced security. The flow involves setting up, enabling, verifying, and disabling the feature.
2FA Management Flow
This flowchart outlines the steps a user takes to enable 2FA on their account.
2FA Endpoints
| Endpoint | Method | Description | Auth Required |
|---|---|---|---|
/auth/2fa/setup | POST | Initiates 2FA setup by generating a secret and QR code. | Yes |
/auth/2fa/enable | POST | Verifies a TOTP code and enables 2FA for the user, generating backup codes. | Yes |
/auth/2fa/verify | POST | Verifies a TOTP or backup code during login. | No (uses temp token) |
/auth/2fa/disable | POST | Disables 2FA after verifying the user's password. | Yes |
Sources: services/api/src/routes/auth.ts:282-519
Authorization (RBAC)
Authorization is handled by checking the role property within the JWT payload. The system defines a special pre-handler, app.requireAdmin, to protect routes that should only be accessible to administrators.
This handler checks if the authenticated user's role is ADMIN. If not, it returns a 403 Forbidden error.
Sources: services/api/src/routes/admin.ts:7, [services/api/src/server.ts]
// In server.ts, a decorator is added to the Fastify instance
app.decorate("requireAdmin", async (request: FastifyRequest, reply: FastifyReply) => {
try {
if (request.user.role !== "ADMIN") {
return reply.status(403).send({ error: "Forbidden: Admin access required" });
}
} catch (err) {
reply.send(err);
}
});
// Example usage in admin.ts
app.get("/admin/stats", { preHandler: app.requireAdmin }, async () => {
// ... admin logic
});
Frontend Integration
The services/web React application uses a React Context (AuthContext) to manage authentication state globally.
AuthContext
The AuthContext provides:
- State:
token,user,isAuthenticated,isLoading. - Functions:
login(),register(),logout(). - Token Storage: The JWT is persisted in the browser's
localStorageto maintain the session across page reloads. - Authenticated API Calls: The context likely provides a configured
axiosinstance or a wrapper function that automatically includes theAuthorization: Bearer <token>header in API requests.
Sources: services/web/src/context/AuthContext.tsx
This diagram shows how the frontend AuthContext interacts with localStorage and the API.
Authentication API Endpoints
The following table summarizes all endpoints defined in services/api/src/routes/auth.ts.
| Endpoint | Method | Description | Auth Required |
|---|---|---|---|
/auth/register | POST | Registers a new user. | No |
/auth/verify-email | POST | Verifies a user's email address using a token. | No |
/auth/resend-verification | POST | Resends the email verification link. | No |
/auth/login | POST | Authenticates a user and returns a JWT. | No |
/auth/change-password | POST | Allows an authenticated user to change their password. | Yes |
/auth/2fa/setup | POST | Generates a secret and QR code for 2FA setup. | Yes |
/auth/2fa/enable | POST | Enables 2FA after verifying a TOTP code. | Yes |
/auth/2fa/verify | POST | Verifies a TOTP code during a 2FA login flow. | Temp Token |
/auth/2fa/disable | POST | Disables 2FA for the user's account. | Yes |
/auth/me | GET | Retrieves the profile of the currently authenticated user. | Yes |
Sources: services/api/src/routes/auth.ts