This document describes the security mechanisms, authentication flows, and best practices implemented in the Clean Elysia API.
- Authentication & Authorization
- Password Security
- Token Management
- Rate Limiting
- Input Validation
- Security Headers
- RBAC (Role-Based Access Control)
- Best Practices
The API uses JWT (JSON Web Tokens) for authentication. The authentication flow works as follows:
POST /auth/register
{
"name": "John Doe",
"email": "john@example.com",
"password": "SecurePass123!"
}
- Password is hashed using bcrypt before storage
- Email verification link is sent to the user
- User account is created but marked as unverified
POST /auth/verify-email
{
"token": "verification_token_from_email"
}
- Verifies user's email address
- Activates the account for login
POST /auth/login
{
"email": "john@example.com",
"password": "SecurePass123!"
}
Response:
{
"success": true,
"message": "Login successful",
"data": {
"user": {
"id": "uuid",
"name": "John Doe",
"email": "john@example.com",
"role": "user"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}Include the JWT token in the Authorization header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Protected routes require authentication via the AuthPlugin. It validates JWT tokens and loads user information into the request context.
Usage:
import { AuthPlugin } from "@plugins";
// Apply to protected routes
app.use(AuthPlugin).get("/protected", ({ currentUser }) => {
return { user: currentUser };
});For Login (Basic Validation):
- Minimum 8 characters
- Maximum 128 characters
For Registration/Password Changes (Strong Validation):
- Minimum 8 characters
- At least one uppercase letter (A-Z)
- At least one lowercase letter (a-z)
- At least one number (0-9)
- At least one special character (!@#$%^&*()_+-=[]{}|;:,.<>?)
- Passwords are never stored in plain text
- Passwords are hashed using bcrypt with automatic salt generation
- Hash rounds are configured for optimal security/performance balance
POST /auth/forgot-password
{
"email": "user@example.com"
}
- Sends password reset link to email
- Token expires after a configured time period (default: 1 hour)
- For security, same response is returned whether email exists or not
POST /auth/reset-password
{
"token": "reset_token_from_email",
"password": "NewSecure123!"
}
- Validates reset token
- Ensures new password meets strong password requirements
- Invalidates old tokens after successful reset
{
"sub": "user_uuid",
"email": "user@example.com",
"role": "user",
"iat": 1640000000,
"exp": 1640086400
}- Access Token: Short-lived token for API requests (default: 24 hours)
All authenticated endpoints validate:
- Token signature
- Token expiration
- User existence
- User active status
Tokens can be invalidated through:
- Password change (invalidates all existing tokens)
- Admin action (account suspension)
Rate limiting is implemented to prevent abuse and brute force attacks.
| Endpoint Type | Limit | Window |
|---|---|---|
| General API | 100 requests | 60 seconds |
{
"success": false,
"message": "Too many requests. Please try again later.",
"data": null
}All API inputs are validated using TypeBox schemas with automatic OpenAPI integration.
- Type Validation: Ensures correct data types
- Format Validation: Email format, UUID format, etc.
- Business Rules: Password strength, min/max lengths
{
"success": false,
"message": "Validation Error",
"data": null,
"errors": {
"email": ["Must be a valid email address"],
"password": ["Password must be at least 8 characters"]
}
}| Field Type | Validation Rules |
|---|---|
| Valid email format, max 255 chars | |
| Password (basic) | Min 8 chars, max 128 chars |
| Password (strong) | Min 8 chars + complexity rules |
| UUID | Valid UUID v4 format |
| Name | Min 1 char, max 255 chars |
The API uses elysia-helmet to set security headers on all responses:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'
CORS is configured via @elysiajs/cors and the ALLOWED_HOST environment variable:
Development:
- Allows all origins for testing (
*) - Credentials enabled
Production:
- Whitelist of allowed origins
- Credentials enabled for trusted domains only
The system implements hierarchical role-based access:
- Super Admin: Full system access
- Admin: Manage users and settings
- User: Standard user access
Granular permissions control specific actions:
user list: View usersuser create: Create usersuser edit: Update usersuser delete: Delete usersrole list/create/edit/delete: Manage rolespermission list/create/edit/delete: Manage permissions
Routes can be protected with permission requirements:
import { PermissionGuard } from "@guards";
app.use(PermissionGuard("user create")).post("/users", createUserHandler);Routes can be protected with role requirements:
import { RoleGuard } from "@guards";
app.use(RoleGuard("superuser")).post("/admin/action", adminHandler);-
Store tokens securely
- Use httpOnly cookies or secure storage
- Never expose tokens in URLs
- Clear tokens on logout
-
Handle token expiration
- Implement token refresh logic
- Gracefully handle 401 unauthorized responses
- Redirect to login when tokens expire
-
Use HTTPS
- Always use HTTPS in production
- Never send credentials over HTTP
-
Implement rate limit handling
- Respect rate limit headers
- Implement exponential backoff on failures
- Cache responses when appropriate
-
Never log sensitive data
- No passwords in logs
- Mask sensitive fields
- Use structured logging
-
Keep dependencies updated
- Regular security updates
- Monitor vulnerability databases
-
Use environment variables
- Never hardcode secrets
- Use different keys per environment
- Rotate keys regularly
- JWT-based authentication
- Password hashing (bcrypt)
- Email verification
- Password reset with tokens
- Token expiration
- Secure password requirements
- Role-based access control (RBAC)
- Permission-based guards
- Plugin-based authentication
- Protected routes
- TypeBox schema validation
- OpenAPI integration
- Detailed error messages
- Type safety
- Request rate limiting
- Configurable limits
- CORS configuration
- Helmet security headers
- Content type validation
- Structured logging (Pino)
- Request ID tracking
- Error tracking
For security issues or vulnerabilities:
- Do not open public issues
- Use responsible disclosure practices
- Allow reasonable time for fixes
For general security questions:
- Review this documentation
- Check the API documentation
Last Updated: February 2026 Version: 1.0.0