Why isn’t validate function on a NestJs strategy class not being called?

I’m trying to properly activate JwtStategy in a NestJs/Angular project of mine. The problem is that no matter what I do, stategy’s validate() function is not called. Users should be validated in a Microsoft Active Directory server.
Here’s my code:

NestJs Backend:

Authentication Module:

import { Module } from '@nestjs/common';

import { AuthenticationService } from './authentication.service';
import { ConfigService } from '@nestjs/config';
import { JwtModule, JwtService } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';

import { MyProjectStrategy } from './genius.strategy';
import { AuthenticationController } from './authentication.controller';
import { UsersService } from 'src/users/users.service';

const jwtFactory = {
    useFactory: async (configService: ConfigService) => {
        return {
            privateKey: configService.get<string>('JWT_PRIVATE_KEY_EC512', ''),
            publicKey: configService.get<string>('JWT_PUBLIC_KEY_EC512', ''),
            signOptions: {
                expiresIn: configService.get('JWT_EXP_H'),
            },
        };
    },
    inject: [ConfigService],
};


@Module({
    imports: [
        JwtModule.registerAsync(jwtFactory),
        PassportModule
    ],
    controllers: [AuthenticationController],
    providers: [
        MyProjectStrategy, 
        ConfigService,      
        AuthenticationService,
        UsersService
    ],
    exports: [
        AuthenticationService,
        JwtModule, 
        MyProjectStrategy, 
        PassportModule
    ],
})
export class AuthenticationModule {}

Authenticaton Controller:

import { Controller, Get, Post, Param, Body, UseGuards } from '@nestjs/common';

import { AuthenticationService } from './authentication.service';

@Controller('authentication')
export class AuthenticationController {

    constructor(
        private readonly authenticationService: AuthenticationService
    ) { }

    @Post('/authenticate/:username')
    async postAutenticar(@Param('username') username: string, @Body() body: any): Promise<any> {
        if (!body.password) 
            return { status: 500, msg: "Can't login - password or organization not informed", error: {}};

        return await this.authenticationService.authenticate(username, body.password);
    }
}

Autentication Service:

import { Injectable } from '@nestjs/common';
import 'dotenv/config';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';

import { User } from '../common/models/user.model';
import { UsersService } from 'src/users/users.service';

export class AuthenticationError extends Error { }

@Injectable()
export class AuthenticationService {

    constructor(
        private usersService: UsersService,
        private jwtService: JwtService,
        private configService: ConfigService
    ) {
    }

    async authenticate(username: string, password: string): Promise<{ accessToken: string}> {
        let loggedUser: boolean | User = (await this.usersService.logUser(username, password)).result;
        if (!(loggedUser instanceof User))
            throw new AuthenticationError("Invalid user");

        const accessToken: string = this.jwtService.sign(loggedUser.toPlainObj(), {
            secret: this.configService.get('JWT_PRIVATE_KEY_EC512', ''),
            algorithm: 'ES512',
            expiresIn: 10000
        });

        if (loggedUser instanceof User)
            console.log('User ', loggedUser.name, ' logged.');
        return { accessToken };
    }

    async refreshToken(
        username: string, password: string,
    ): Promise<{ accessToken: string }> {

        let loggedUser = this.authenticate(username, password);
        if (loggedUser instanceof User)
            console.log('User ', loggedUser.name, ' updated token.');
        return loggedUser;
    };

}

Stategy:

import { Injectable, UnauthorizedException, Request } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { User } from 'src/common/models/user.model';
import { UsersService } from 'src/users/users.service';

@Injectable()
export class MyProjectStrategy extends PassportStrategy(Strategy, 'myproject-jwt') {
    constructor(
        private usersService: UsersService,
        private configService: ConfigService
    ) {
        super({
            secretOrKey: configService.get<string>('JWT_PUBLIC_KEY_EC512', ''),
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            ignoreExpiration: false,
            algorithms: ['ES512'],
        });
    }

    async validate(user: any): Promise<boolean> {
        console.log('validate');
        return !!(await this.usersService.logUser(user.username, user.password)).result;
    }
}

Guard:

import { CanActivate, ExecutionContext, HttpException, HttpStatus, Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'
import { JwtService } from '@nestjs/jwt';
import { ExtractJwt, Strategy } from 'passport-jwt';

import { AuthenticationService } from './authentication.service';
import { Request } from 'express';
import 'dotenv/config';
import { User } from 'src/common/models/user.model';
import { TokenExpiredException } from './token-expired.exception';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class MyProjectGuard extends AuthGuard('myproject-jwt') {

    constructor(
        private readonly authenticationService: AuthenticationService,
        private jwtService: JwtService,
        private configService: ConfigService
    ) {
        super({
          jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('JWT'),
          secretOrKey: configService.get<string>('JWT_PUBLIC_KEY_EC512', ''),
        });
    }

    
    async canActivate(context: ExecutionContext): Promise<boolean> {
        console.log('canActivate');
        const request: Request = context.switchToHttp().getRequest();

        const token = request.header('Authorization');
        if (!token) 
            throw new HttpException('Header Bearer <token> missing', HttpStatus.UNAUTHORIZED);

        const parts = token.split(' ');
        if (parts.length !== 2 || parts[0] !== 'Bearer') 
            throw new HttpException('Header Bearer <token> invalid', HttpStatus.UNAUTHORIZED);

        let user = new User(this.jwtService.decode(parts[1]));

        if (user.administrator)
            return true;
        else
            throw new HttpException('Usuário não autorizado', HttpStatus.UNAUTHORIZED);
    }
    
    handleRequest(err, user, info) {
        console.log('handleRequest')
        if (err || !user) {
            if (info ? info.message.indexOf('expired') >= 0 : false)
                throw new TokenExpiredException();
            else 
                throw err || new UnauthorizedException();
        }
        return user;
    }   
}

Users controller:

import { Controller, Get, Post, Param, Body, UseGuards } from '@nestjs/common';

import { UsersService } from './users.service';
import { MyProjectGuard } from '../authentication/genius.guard';

@Controller('users')
export class UsersController {

    constructor(
        private readonly usersService: UsersService
    ) { }

    @UseGuards(MyProjectGuard)
    @Get('/read/:username')
    async getObter(@Param('username') username: string): Promise<any> {
        console.log('OK');
        return this.usersService.readUser(username);
    }
    
    @Post('/log/:username')
    async postLogar(@Param('username') username: string, @Body() body: any): Promise<any> {
        if (!body.password) 
            return { status: 500, msg: "Can't login - password or organization not informed", error: {}};

        return await this.usersService.logUser(username, body.password);
    }
}

Users service:

import { Injectable } from '@nestjs/common';
import { Client, Attribute, Change } from 'ldapts';
import fs from 'fs';
import 'dotenv/config';

import { User } from 'src/common/models/user.model';
import { BeResult } from 'src/common/models/beResult.model';

@Injectable()
export class UsersService {

    client: Client;
    searchDN: string;
    baseDN: string;
    bindDN: string = process.env.LDAP_BIND_DN;
    admPwd: string = process.env.LDAP_BIND_PWD;

    constructor() {
        this.searchDN = process.env.LDAP_USERS_BASE_DN;
        this.baseDN = process.env.LDAP_USERS_BASE_DN;
        let certCA = fs.readFileSync(process.env.LDAP_CERT_CA_PATH);
        this.client = new Client({
            url: process.env.LDAP_URL,
            timeout: 3000,
            connectTimeout: 5000,
            tlsOptions: {
                ca: [certCA],
            }
        });

        this.client.bind(this.bindDN, this.admPwd);
    }

    (...)       

    async _readUser(username) {
        const { searchEntries } = await this.client.search(this.searchDN, {
            scope: 'sub',
            filter: `&(objectClass=person)(sAMAccountName=${this.susername(username)})`,
            attributes: ['objectClass', 'sn', 'name', 'description', 'sAMAccountName', 'mail', 'telephoneNumber', 'street', 'title', 'unicodePwd'],
        });
        let result = searchEntries.length ? this.convertUserLdapToUser(searchEntries[0]) : {}; 
        return result;
        
    } 

    async readUser(username: string) {
        return { status: 200, msg: 'OK', result: await this._readUser(username) }
    }
    
    async logUser(username: string, password: string) : Promise<BeResult> {
        let res: boolean = false;
        let error: any;
        let user: User | {};
        try {
            await this.client.unbind();
            await this.client.bind(`mydomain\${this.susername(username)}`, password);
            res = true; 
        } catch(e: any) {
            if (e.name != 'InvalidCredentialsError')
                error = e;
        } finally {
            if (res) {
                user = await this._readUser(username);
            }
            await this.client.unbind();
            await this.client.bind(this.bindDN, this.admPwd);
            return erro ? { status: 500, msg: "Can't log", erro } :
                { status: 200, msg: res ? "Logged user" : "Invalid Credential", result: res ? user : false };
        }
    }

    (...)

}

Frontend:

Users service:

import { Injectable } from '@angular/core';
import { environment } from '../../environments/environment';
import { User } from '../model/user.model';
import { BehaviorSubject, catchError, map, Observable, tap } from 'rxjs';
import { BeResult } from '../model/beResult.model';
import { HttpClient } from '@angular/common/http';
import { ProcessHTTPMsgService } from './process-httpmsg.service';
import { jwtDecode } from "jwt-decode";

@Injectable({
    providedIn: 'root'
})
export class UsersService {

    token: string = '';

    constructor(
        private http: HttpClient,
        private processHTTPMsgService: ProcessHTTPMsgService,
    ) {
    }

    private currentUserSource = new BehaviorSubject<User>(new User({.name: 'My Name (test)', organizacao: 'My Organization (test)' }));
    currentUser = this.currentUserSource.asObservable();

    getHeaders() {
        return { headers: { 'Authorization': `Bearer ${this.token}` } };
    }

    setCurrentUser(user: User) {
        this.currentUserSource.next(user);
    }

    (...)    

    log(user: User): Observable<boolean | User | {}> {
        let body: any = user;
        body['headers'] = this.getHeaders();

        return this.http
            .post<BeResult>(`${environment.apiUrl}/authentication/authenticate/${user.username}`, body)
            .pipe(map(r => this.convertResultado(r)), catchError(this.processHTTPMsgService.handleError));
    }

    public getUserByUsername(user: User): Observable<User> {
        
        return this.http
            .get<BeResult>(`${environment.apiUrl}/users/read/${user.username}`, this.getHeaders())
            .pipe(tap(console.log), map(r => r.result), catchError(this.processHTTPMsgService.handleError));
    }

}

Login component:

import { Component } from '@angular/core';
import { InputTextModule } from 'primeng/inputtext';
import { CardModule } from 'primeng/card';
import { ButtonModule } from 'primeng/button';
import { Router, RouterLink } from '@angular/router';
import { FormsModule } from '@angular/forms';

import { UsersService } from '../../common/services/users.service';
import { User } from '../../common/model/user.model';

@Component({
  selector: 'app-login',
  standalone: true,
  imports: [InputTextModule, CardModule, ButtonModule, RouterLink, FormsModule],
  templateUrl: './login.component.html',
  styleUrl: './login.component.scss'
})
export class LoginComponent {
    
    constructor(
        private usersService: UsersService,
        private router: Router
    ) { }

    
    username: string = '';
    password: string = '';  

    log() {
        let user = new User();
        user.username = this.username;
        user.password = this.password;
        this.usersService.log(user).subscribe((user: boolean | User | {}) => {
            if (user instanceof User && user.administrator) {
                this.usersService.setCurrentUser(user);
                this.router.navigate(['one-user']);
            }
        })
    }
    
}

Component that tried to access a protected route:

import { Component } from '@angular/core';
(...)
import { UsersService } from '../../common/services/users.service';
import { User } from '../../common/model/user.model';

@Component({
  selector: 'app-one-user',
  standalone: true,
  imports: [InputTextModule, InputMaskModule, PasswordModule, DividerModule, ButtonModule],
  templateUrl: './one-user.component.html',
  styleUrl: './one-user.component.scss'
})
export class UmUserComponent {
    
    constructor(
        private usersService: UsersService
    ) {
        
    }
    
    gravar() {
        let user = new User({ username: 'myusername'});
        
        this.usersService.getUserByUsername(user).subscribe((user: User) => {
            console.log('user', user)
        })
    }
}

I’ve seen several questions regarding this subject and can’t see anyone that is doing nothing very different of what I’m doing. What’s the problem here ?

In one of the questions I saw, says that validate() is only called when the payload is correct. I’m reazonably certain the payload is correct as it apears correctly when I debug it.
Any ideas ?

I had to translate several parts of the code to english and revised it 3x. Please apologize me if I missed something.