// source https://github.com/nestjs/nest/issues/1254
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { bindNodeCallback, Observable, of } from 'rxjs';
import { JwtPayload } from './jwt-payload.interface';
import * as jwt from 'jsonwebtoken';
import { catchError, flatMap, map } from 'rxjs/operators';
import { User } from '../user/user.entity';
import { AuthService } from './auth.service';
@Injectable()
export class JwtWsGuard implements CanActivate {
constructor(
protected readonly authService: AuthService,
) {
}
canActivate(
context: ExecutionContext,
): Observable<boolean> {
const data = context.switchToWs().getData();
const authHeader = data.headers.authorization;
const authToken = authHeader.substring(7, authHeader.length);
const verify: (...args: any[]) => Observable<JwtPayload> = bindNodeCallback(jwt.verify) as any;
return verify(authToken, process.env.JWT_SECRET_KEY, null)
.pipe(
flatMap(jwtPayload => this.authService.validateUser(jwtPayload)),
catchError(e => {
console.error(e);
return of(null);
}),
map((user: User | null) => {
const isVerified = Boolean(user);
if (!isVerified) {
throw new UnauthorizedException();
}
return isVerified;
}),
);
}
}
// Where on the client you would authenticate by passing 'dummy' headers in the data object like so:
const websocket = this.websocketService
.createConnection(`ws://localhost:8080`);
const jsonWebToken = getJwtSomehow();
websocket.subscribe(
(msg) => console.log('message received: ', msg),
(err) => console.log(err),
() => console.log('complete')
);
websocket.next({
event: 'YOUR_EVENT_NAME',
data: {
// ...
headers: {
authorization: `Bearer ${jsonWebToken}`
}
},
});