Rate Limit Password Inputs
In this guide, we'll explore how to set up a rate-limiting feature for password attempts in Xano. This feature suspends users from logging in for a specified duration if they exceed a certain number of failed login attempts within a given time frame. It helps prevent brute-force attacks and reduces server load from excessive invalid requests.
Step 1: Create a Login Attempts Table
Start by creating a table called login_attempts to log all login attempts. This table should have the following columns:
user_id(reference to the user table)success(boolean indicating whether the login attempt was successful or not)created_at(timestamp of the login attempt)
Additionally, add two columns to your users table:
temp_suspension(boolean indicating if the user is currently suspended)temp_timeout_end(timestamp of when the suspension period ends)
Step 2: Update the Login Endpoint
In your auth/login endpoint, modify the logic to log each login attempt in the login_attempts table. Add a record with the user_id and success (set to true for successful logins and false for failed attempts).
// Successful login attempt
add_record(login_attempts, {
user_id: user.id,
success: true
});
// Failed login attempt
add_record(login_attempts, {
user_id: user.id,
success: false
});
Step 3: Create a Trigger
Next, create a trigger that checks for failed login attempts within a specific time frame (e.g., 5 attempts in the last 60 seconds). If the condition is met and the user is not already suspended, update the users table to set temp_suspension to true and temp_timeout_end to the current timestamp plus a specified duration (e.g., 60 seconds).
- Go to your
login_attemptstable and click the three dots in the top-right corner, then select "Triggers" and "Add Database Trigger." - Name the trigger "rate_limit_password_attempts" and set the action to "Insert."
- Add a query to count the number of failed login attempts for the user within the last 60 seconds:
query_all_records(login_attempts, {
filters: [
{
field: 'user_id',
operator: 'equals',
value: new.user_id
},
{
field: 'success',
operator: 'equals',
value: false
},
{
field: 'created_at',
operator: 'greater_than',
value: timestamp_subtract(now(), seconds(60))
},
{
field: 'created_at',
operator: 'less_than',
value: now()
}
]
})
- Check if the count is greater than or equal to 5 (or your chosen threshold) and if the user is not already suspended:
if (login_attempt_count >= 5 && get_record(users, new.user_id).temp_suspension == false) {
edit_record(users, new.user_id, {
temp_suspension: true,
temp_timeout_end: timestamp_add(now(), seconds(60))
});
}
This suspends the user for 60 seconds after the threshold is reached.
Step 4: Create Pre-Middleware
To prevent suspended users from making further login attempts, create pre-middleware that checks the user's suspension status before executing the login endpoint logic.
- Go to the "Library" section and click "Add Middleware."
- Name the middleware "rate_limit_password_attempts" and set the response type to "Replace Exception Critical and Safe."
- In the middleware function, retrieve the user record based on the provided email:
user = get_record(users, {email: vars.email});
- Add a precondition to check if the user is not suspended:
precondition(user.temp_suspension == false, "Too many requests. Please try again later.", 429);
- Attach the pre-middleware to your
auth/loginendpoint by going to the endpoint settings, clicking "Customize" under "Middleware," and adding the pre-middleware you just created.
Step 5: Create a Background Task
Finally, create a background task to automatically unsuspend users after the timeout period has expired.
- Go to the "Background Tasks" section and click "Add Background Task."
- Name the task "rate_limit_password_attempts."
- Query all users where
temp_suspensionistrueandtemp_timeout_endis less than the current time:
query_all_records(users, {
filters: [
{
field: 'temp_suspension',
operator: 'equals',
value: true
},
{
field: 'temp_timeout_end',
operator: 'less_than',
value: now()
}
]
});
- Loop through the retrieved users and update their
temp_suspensiontofalseandtemp_timeout_endtonull:
foreach(users, user => {
edit_record(users, user.id, {
temp_suspension: false,
temp_timeout_end: null
});
});
- Set the task to run every 10 seconds (or your desired interval) and enable it.
With these steps completed, your Xano application now has a rate-limiting feature for password attempts, improving security and preventing potential brute-force attacks.
Sign up for Xano.
100,000+ people build on Xano.
Start today and scale to millions.