Create custom login route

Hi,

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:

async login(ctx) {
    try {
      const { identifier, password } = ctx.request.body;
      const ip = ctx.request.ip;
      const authenticatedUser = strapi.plugins['users-permissions'].services.auth.login({
        identifier,
        password,
      });
      return ctx.send({ user: authenticatedUser });
    } catch (error) {
      return ctx.send({ error: 'Authentication failed' }, 401);
    }
  },

Hope someone knows a little bit more about this :smiley:

Can you please share a screenshot of your controller and file structure.

My file structure is like:
/api/authentication/controllers/authentication.js
/api/authentication/routes/routes.js

Below is a screenshot of my controller and routes.js

Okay looks proper.
Did you debug the code or checked what is the error? (Please share the error)

It returns me

{
    "error": "Authentication failed"
}

I am reading a thread here: Extending /auth/local/register endpoint with custom logic - #5 by N_H

Maybe I should give something like this a try?

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.

Works for me! Thankyou!

1 Like

Hello @jarmo, I think the error you had is that you did not set the await when you called user-permissions.login

then it should be

const authenticatedUser = await strapi.plugins['users-permissions'].services.auth.login({
   identifier,
   password,
});

I came here looking if I could do a custom login, thanks for that.

Hi @jarmo ,

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.

Hi @ayyaoa ,

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