I want to create a custom login route instead of /api/auth/local.
Reason for this is that I want to check if a specific IP tried to login more than 3 times in 10 minutes for safety reasons. After the third time I want to block this IP for 15 minutes so basically I want to do some extra checks. Also creating a middleware for the login process would be nice. I have created a custom route and a controller. I can’t get it work. This is my code in my controller right now:
Your requirement is different by creating a new route here.
I have developed user authentication related APIs custom work Link. Check this and follow if it helps.
Can you please share your complete implementation? I have the same requirement, to implement a 10 mins blocker if there are 5 failed login attempts in under a minute.
I have created a custom content type called Blacklist.
From my React front-end application I sent a clientId with the login:
const generateClientId = () => {
var id = localStorage.getItem('clientId');
if (id) {
setClientId(id);
} else {
id = generateUniqueId();
localStorage.setItem('clientId', id);
setClientId(id);
}
return id;
};
const generateUniqueId = () => {
// Generate a unique identifier using any method you prefer
// Here's an example using the current timestamp and a random number
return `${Date.now()}-${Math.floor(Math.random() * 100000)}`;
};
const response = await api.post('/auth/local', {
"identifier": identifier,
"password": password,
"clientId": clientId
});
In my custom login in the Strapi application I have:
require('dotenv').config();
const moment = require('moment');
require('moment/locale/nl');
module.exports = (plugin) => {
plugin.controllers.auth.callback = async (ctx) => {
const provider = ctx.params.provider || 'local';
const params = ctx.request.body;
//Check if ip is blacklisted
var blacklisted = await strapi.db.query("api::blacklist.blacklist").findOne({
where: {
client_id: params.clientId
},
})
if (blacklisted && blacklisted.login_attempts > 3) {
const now = new Date();
const targetDate = new Date(blacklisted.updatedAt);
const timeDifference = now - targetDate;
const minutes = Math.floor(timeDifference / (1000 * 60));
const defMinutes = 15 - minutes;
const minutesString = defMinutes === 1 ? 'minut' : 'minutes';
return ctx.send({ error: 'You've been blocked for 15 minutes for trying to many wrong login attempts. In ' + defMinutes + ' ' + minutesString + ' you will be able to try again.' }, 403);
}
if (provider === 'local') {
//Check if user exist
const user = await strapi.db.query("plugin::users-permissions.user").findOne({
where: {
$or: [
{
username: params.identifier,
},
{
email: params.identifier,
},
],
},
})
//If user does not exist return error
if (!user) {
return ctx.send({ error: 'Geen gebruiker gevonden' }, 400);
}
//If user exist but is not confirmed return error
if (user.confirmed !== true) {
return ctx.send({ error: 'Account is nog niet geactiveerd' }, 403);
}
//If user exist but is blocked
if (user.blocked === true) {
return ctx.send({ error: 'Account is geblokkeerd' }, 403);
}
//Validate password
var validPassword = await strapi.plugins['users-permissions'].services.user.validatePassword(params.password, user.password);
//If password is invalid return error and create or update blacklisted clientId, else get JWT
if (!validPassword) {
if (blacklisted) {
await strapi.entityService.update('api::blacklist.blacklist', blacklisted.id, {
data: {
last_identifier: params.identifier,
login_attempts: blacklisted.login_attempts + 1,
},
});
} else {
await strapi.entityService.create('api::blacklist.blacklist', {
data: {
last_identifier: params.identifier,
client_id: params.clientId,
login_attempts: 1,
},
});
}
return ctx.send({ error: 'Onjuiste combinatie van gebruikersnaam/wachtwoord' }, 403);
} else {
if (blacklisted) {
await strapi.db.query('api::blacklist.blacklist').delete({
where: { id: blacklisted.id },
});
}
return ctx.send({
jwt: strapi.plugins['users-permissions'].services.jwt.issue({
id: user.id
}),
user
});
}
}
}
return plugin;
}
In my cron tasks I run a cron job to clear the blacklisted clientId’s