Authentication and authorization
TaskForge API uses a layered approach to protect every endpoint.bcrypt password hashing
User passwords are hashed with bcrypt before storage. Plain-text passwords are never persisted.
JWT tokens
Access tokens expire after 1 hour (
JWT_ACCESS_TOKEN_EXPIRES=3600). Refresh tokens expire after 30 days (JWT_REFRESH_TOKEN_EXPIRES=2592000). Both values are configurable via environment variables.Refresh token revocation
Refresh tokens are stored in the
refresh_tokens database table. Logging out revokes the current token. Changing a password revokes all tokens for the user.RBAC
Two roles:
admin and user. Role-specific middleware (app/middleware/rbac.py) restricts access to admin-only endpoints such as user management.JWT token flow
Tokens are transmitted in theAuthorization header using the Bearer scheme:
sub. When an access token expires, use the refresh token to obtain a new one:
Rate limiting
Flask-Limiter applies rate limits to all endpoints. The defaults, configurable viaRATELIMIT_DEFAULT in .env, are:
- 200 requests per day
- 50 requests per hour
ProductionConfig and always disabled in TestingConfig. Rate limit counters are stored in memory by default (RATELIMIT_STORAGE_URL=memory://). In a multi-worker deployment, use a Redis storage URL to share counters across workers:
.env
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Input validation
All request payloads are validated by functions inapp/utils/validators.py before reaching service or model code. Validation covers:
- Required field presence
- String length and format (email addresses, usernames, passwords)
- Enum membership (task status and priority values)
- Date format correctness
- Numeric range (pagination parameters)
CORS configuration
Cross-origin requests are restricted to the origins listed inCORS_ORIGINS. Configure this variable to match your frontend’s origin:
.env
GET, POST, PUT, PATCH, DELETE, and OPTIONS. The allowed headers are Content-Type and Authorization. All configuration is applied in app/config.py:52-54.
HTTPS
HTTPS is enforced by Azure App Service in the production environment. The platform terminates TLS and forwards requests to the application over HTTP internally. No additional Flask configuration is required for TLS termination.Refresh token security
Refresh tokens are stored in therefresh_tokens table with an expires_at timestamp. The application enforces the following:
| Action | Effect |
|---|---|
POST /api/auth/logout | Revokes the current refresh token only |
PUT /api/auth/change-password | Revokes all refresh tokens for the authenticated user |
| Token expiry | Expired tokens are rejected even if not explicitly revoked |
Environment security
SECRET_KEY and JWT_SECRET_KEY must be cryptographically random values. The placeholders in .env.example must never be used in production.
Generate secure values:
SECRET_KEY and JWT_SECRET_KEY respectively.
SonarCloud analysis
Thecode-quality.yml workflow runs SonarCloud on every push and pull request to main, master, and develop. It analyzes:
- Bugs — code that is likely incorrect
- Vulnerabilities — security weaknesses (OWASP categories)
- Code smells — maintainability issues
- Coverage — percentage of lines exercised by tests
- Duplications — copy-pasted code blocks
Security checklist
Credentials and secrets
Credentials and secrets
SECRET_KEYis a cryptographically random value (at least 32 bytes)JWT_SECRET_KEYis a cryptographically random value (at least 32 bytes), different fromSECRET_KEY.envis listed in.gitignoreand has never been committed- Azure App Service application settings are used instead of a
.envfile in production AZURE_SQL_PASSWORDuses a strong password meeting Azure SQL complexity requirements
Flask configuration
Flask configuration
FLASK_ENVis set toproductionDEBUGisFalse(enforced byProductionConfig)TESTINGisFalse(enforced byProductionConfig)
Database
Database
- All four
AZURE_SQL_*variables are set so the application does not fall back to SQLite - The Azure SQL firewall allows only the App Service outbound IP addresses
scripts/init_db_azure.sqlwas used to initialize the schema (notdb.create_all()alone)
Network and transport
Network and transport
- HTTPS is enforced on the Azure App Service (HTTPS Only setting enabled)
CORS_ORIGINSis restricted to your production frontend origin, notlocalhostRATELIMIT_ENABLEDistrueRATELIMIT_STORAGE_URLpoints to a shared Redis instance if running multiple Gunicorn workers
Tokens and sessions
Tokens and sessions
JWT_ACCESS_TOKEN_EXPIRESis set to an appropriate short lifetime (default 3600 seconds)JWT_REFRESH_TOKEN_EXPIRESis appropriate for your session requirements (default 2592000 seconds)- Users are informed to log out when using shared devices (revokes the active refresh token)
Code quality
Code quality
- SonarCloud Quality Gate is passing for the current codebase
- No open vulnerabilities or security hotspots in the SonarCloud dashboard
- Test coverage is above 70% (enforced by the deployment pipeline)