Aller au contenu principal

5 - Authentification via WebSockets

Passer le jeton avec les requêtes (Angular)

Du côté client, vous devrez passer le jeton stocké dans le localstorage à chaque requête de WebSocket. Pour ce faire, vous devez simplement modifier votre configuration de Socket.io dans app.module de la façon suivante:

// Récupère le jeton du localstorage
const token = localStorage.getItem('access_token');
const config: SocketIoConfig = {
url: 'http://localhost', options: {
transportOptions: {
polling: {
// Ajoute dans l'en-tête des requêtes le jeton
extraHeaders: {
Authorization: `Bearer ${token}`
}
}
}
}
};

@NgModule({
//...

Récupérer et traiter le jeton côté serveur

Dans le module d'authentification, on peut ajouter un guard spécialisé pour les websocket. Ce guard récupère le jeton de l'en-tête, le décode et récupère l'utilisateur associé pour l'associer à la variable client disponible dans les gateway.

  1. Dans votre module associé aux messages (ex.: messages.module.ts), importez AuthModule et la configuration pour JWT.
@Module({
imports: [
/...
AuthModule,
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
secret: configService.get('JWT_SECRET'),
signOptions: { expiresIn: configService.get('JWT_EXPIRES_IN') },
}),
}),
],
/...
  1. Ajouter un guard dans le module auth (ex.: ws-jwt-auth-guard.ts)
    src/auth/ws-jwt-auth-guard.ts
    @Injectable()
    export class WsJwtAuthGuard implements CanActivate {
    constructor(private jwtService: JwtService) {}

    async canActivate(context: ExecutionContext): Promise<boolean> {
    const client = context.switchToWs().getClient();
    const authHeader = client.handshake.headers.authorization;

    if (!authHeader) throw new UnauthorizedException('En-tête d\'authentification manquante');

    const [bearer, token] = authHeader.split(' ');

    if (bearer !== 'Bearer' || !token) throw new UnauthorizedException('Format de jeton invalide');

    try {
    const payload = this.jwtService.verify(token);
    client.user = await Utilisateur.findOne({
    where: {
    id: payload.sub,
    }
    });
    if(client.user)
    return true;
    else
    return false;
    } catch (e) {
    throw new UnauthorizedException('Jeton invalide ou expiré');
    }
    }
    }
  2. Dans le gateway, ajouter la référence au guard via un décorateur
    @WebSocketGateway(80, { cors: true })
    @UseGuards(WsJwtAuthGuard)
    export class PixelsEventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
  3. Ensuite, dans les fonctions du type handleMessage, qui reçoivent en paramètre client, il est possible d'accéder à l'utilisateur si vous en avez besoin. Par exemple, pour associer un message à son auteur!
    @SubscribeMessage('pixel')
    async handleMessage(client: any, data: PixelDataDto) {
    //highglight-next-line
    console.log("Gestion de l'événement pour le client: ", client.user);

    // ...