Rate Limiting & Brute-Force Protection
A detailed look at how CareNova prevents brute-force attacks, tracks authentication attempts, and protects login endpoints.
Written By Dev010
Last updated 19 days ago
CareNova enforces multiple layers of protection on the login endpoint to prevent brute-force attacks, credential stuffing, and automated login attempts. This guide covers exactly how these protections work, what gets recorded, and how admins can investigate suspicious activity.
How Rate Limiting Works
Rate limiting is enforced by checking the login_attempts table before every login attempt is processed. If the threshold is exceeded, the attempt is blocked before Supabase Auth is ever called β no credentials are validated.
The check runs on two dimensions simultaneously:
Both checks run on every login request. Either one triggering is sufficient to block the attempt.
Example scenarios:
A user fails login 5 times in 10 minutes with the same email β blocked by email rule
An automated script tries 10 different email addresses from the same IP β blocked by IP rule
A user fails 4 times, waits 20 minutes, then fails again β not blocked (window expired)
The Login Attempts Table
Every login attempt β successful or failed β is recorded in the login_attempts table:
The rate limit query counts rows where success = false and attempted_at is within the last 15 minutes for the given email or IP.
Successful logins are also recorded. This means a successful login after failed attempts does not reset the counter β the failed attempts remain in the window until they naturally expire after 15 minutes.
What Happens When a Lockout Triggers
When the rate limit threshold is exceeded:
The login attempt is rejected immediately
Supabase Auth is not called β no credential validation occurs
The user receives the message:
Too many failed login attempts.
Please try again in 15 minutes.The blocked attempt is recorded in
auth_audit_logwith event typerate_limitedNo indication is given of whether the password was correct or not
How the Lockout Lifts
There is no manual lockout reset. The lockout lifts automatically when the oldest failed attempt in the 15-minute window falls outside the window β meaning after 15 minutes from the first failed attempt, the count naturally drops below the threshold.
Admins cannot reset a lockout from the dashboard. If a legitimate user is locked out urgently, the quickest resolution is to delete their recent login_attempts records directly in Supabase SQL Editor:
DELETE FROM login_attempts
WHERE email = 'user@yourdomain.com'
AND success = false
AND attempted_at > now() - interval '15 minutes';The Authentication Audit Log
Every significant authentication event is recorded in the auth_audit_log table:
Each entry captures:
Investigating Suspicious Activity
Admins can query the audit log directly in Supabase β Table Editor β auth_audit_log or via SQL Editor.
View all failed login attempts in the last 24 hours:
SELECT email, ip_address, created_at
FROM auth_audit_log
WHERE event = 'login_failed'
AND created_at > now() - interval '24 hours'
ORDER BY created_at DESC;View all activity for a specific email:
SELECT event, ip_address, user_agent, created_at
FROM auth_audit_log
WHERE email = 'user@yourdomain.com'
ORDER BY created_at DESC
LIMIT 50;Find IPs with the most failed attempts:
SELECT ip_address, COUNT(*) as failed_count
FROM auth_audit_log
WHERE event = 'login_failed'
AND created_at > now() - interval '24 hours'
GROUP BY ip_address
ORDER BY failed_count DESC
LIMIT 20;View all rate-limited events:
SELECT email, ip_address, created_at
FROM auth_audit_log
WHERE event = 'rate_limited'
ORDER BY created_at DESC;Session Tracking
Beyond login attempts, CareNova tracks active sessions in the user_sessions table. Each session record includes:
Session activity is updated at most every 5 minutes per session to avoid excessive database writes.
Revoking a Session
If an admin suspects a compromised session, they can revoke it directly in SQL Editor:
UPDATE user_sessions
SET is_revoked = true
WHERE user_id = 'paste-user-uuid-here';The user will be redirected to the login page on their next request.
Automatic Cleanup
The login_attempts and auth_audit_log tables grow over time as authentication events are recorded. The cron job at /api/cron/cleanup-auth handles cleanup:
This keeps the tables lean and ensures the rate limiting queries remain fast β they only scan recent records.
Without the cron job running, these tables will grow indefinitely and rate limit queries will slow down over time. See the Cron Setup guide to configure the cleanup schedule.
Password Policy Enforcement
Rate limiting is complemented by a strict password policy enforced at the application level. All passwords must meet:
Additionally, the following passwords are explicitly blocked:
password, password123, 123456789,
admin123, clinic123, carenova123, welcome1These blocked passwords are rejected regardless of meeting the structural requirements above.
Security Best Practices for Admins
Enforce strong passwords β remind staff of the password policy when onboarding
Review audit logs regularly β check
auth_audit_logweekly for unusual patterns, especiallyrate_limitedeventsMonitor IP addresses β a high volume of failed attempts from one IP indicates an automated attack
Do not share credentials β each staff member should have their own account so audit logs are meaningful
Run the cron job daily β without cleanup, rate limiting degrades over time
Revoke sessions immediately if a staff member's account is suspected compromised
Troubleshooting
Legitimate user locked out:
Delete their recent failed attempts using the SQL query above
The lockout will lift immediately
Rate limiting not working after many failed attempts:
Confirm the cron job is not running too frequently β if cleanup runs every few minutes it could be deleting attempts before the window closes
Confirm the cron schedule is daily, not every few minutes
Check that
login_attemptstable exists and is being written to
Audit log growing very large:
Confirm the cron job is running daily at
/api/cron/cleanup-authCheck Vercel β Functions logs or your cron service for job execution history
Run the cleanup manually once to reduce the backlog:
curl -X GET \
-H "Authorization: Bearer YOUR_CRON_SECRET" \
https://yourdomain.com/api/cron/cleanup-authNext Step
Continue to Roles & Permissions to learn how CareNova controls what authenticated users can see and do inside the platform.