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.
TypeOrmModule.forFeature([User]),Then, inject the repository in the AuthService.
@InjectRepository(User)
private readonly usersRepository: Repository<User>,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.
async validateLocal(email: string, password: string) {}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.
const user = await this.usersRepository.findOne({
select: {
id: true,
password: true,
},
where: { email },
});After that, if the user is not found, we throw an exception.
if (!user) {
throw new UnauthorizedException('Invalid email');
}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.
const isMatch = await this.hashingService.compare(password, user.password);
if (!isMatch) {
throw new UnauthorizedException('Invalid password');
}Finally, we return the id. In a future moment, we'll use it to create a JWT.
return { id: user.id };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.
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private readonly authService: AuthService) {
super({
usernameField: 'email',
});
}
validate(email: string, password: string) {
return this.authService.validateLocal(email, password);
}
}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.
@UseGuards(AuthGuard('local'))
@Post('login')
login(@Request() req) {
return req.user;
}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.
nest g gu auth/guards/local-authAnd with this content, it is the same thing, but we no longer need to insert a generic string.
@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}Now we can replace that guard with this new one.
@UseGuards(LocalAuthGuard)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 anAuthGuardof typelocal. This means that, when this route is accessed, theLocalStrategywill be activated.Inside the
LocalStrategy, it will be checked if therequesthas in itsbodythe login fields:emailandpassword. 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'sidis returned.Finally, this return is appended to the
requestas theuserfield. That's why we can see the user'sidbeing returned. This will be used to perform the login in a future section. But before it, we'll just implement several improvements for better type safety.
Commit - Local strategy
Last updated