Local strategy
Using credentials for identification.
First of all, let's allow the AuthModule
to use a usersRepository
in its context. Therefore, let's insert in its imports
the following.
Then, inject the repository in the AuthService
.
We'll now implement the method validateLocal()
, which has email
and password
as parameters. This name is used for the method because Local is the passport's strategy for identifying a user through credentials, which will afterwards be used in conjunction with this method. Soon we'll understand this better.
Inside this method, first we find a user
with this email
and select only its id
and password
, as only these fields will be used.
After that, if the user
is not found, we throw an exception.
In this case, an UnauthorizedException
is thrown due to the different context in which the user
was not found.
Next, we should check if the provided password
is correct. However, the password
is now hashed before being stored in the database. Hence, we cannot make a simple comparison. We'll have to use the compare()
method from the HashingService
.
If you prefer, you can use a generic error message for a failed login attempt, in order to cloak what a potential malicious user may be getting wrong. Something like 'Invalid credentials'
, for example.
Finally, we return the id
. In a future moment, we'll use it to create a JWT.
Now, we'll use the Local strategy. Create the file auth -> strategies -> local.strategy with following content. The usernameField
field is necessary to indicate the name of the field used to identify the user if the default one isn't used, which is username
.
Strategy
should be imported from passport-local
.
After this, we should go back to the AuthModule
and, in its imports
, add the PassportModule
. Then, in its providers
, the LocalStrategy
. With this, we're creating a flow that will be explained in a few moments.
The next step is to create a login()
route. Let's do it in the AuthController
. Don't worry, everything will be better explained very soon.
This is a POST route, therefore the default
status
if everything goes well is CREATED. However, this route does not create anything. For better semantic correctness, we can override the default status to OK by using over the route@HttpCode(HttpStatus.OK)
.
A Guard protects routes based on certain criteria. It returns a boolean, it being true when access is granted and false when it is denied. We are using on this route the AuthGuard
of type local
, but by using it like this we depend on magic strings. That is, strings that must be specific, untyped values to do something. So, to avoid this, let's create another guard to represent the same thing.
And with this content, it is the same thing, but we no longer need to insert a generic string.
Now we can replace that guard with this new one.
This is a nice start. This route is still not performing the login, but we should stop now to discuss what we have done so far. I know it may have been a lot at once to digest, but let's analyse this entire flow carefully to better understand each step of this process before continuing.
The
login()
route is being protected by anAuthGuard
of typelocal
. This means that, when this route is accessed, theLocalStrategy
will be activated.Inside the
LocalStrategy
, it will be checked if therequest
has in itsbody
the login fields:email
andpassword
. Then, thevalidate()
method will be called, using them as arguments.Then, in the
AuthService
, in the methodvalidateLocal()
, the login attempt is validated through the process already explained previously. If successful, an object containing the user'sid
is returned.Finally, this return is appended to the
request
as theuser
field. That's why we can see the user'sid
beign returned. This will be used to perform the login in a future section. Before it, we'll just implement several improvements for better type safety.
Commit - Local strategy
Last updated