NestJS passport authentication returns 401 when using email for authentication - authentication

I have a problem that seems to be not that uncommon, but the solutions that I found did not work in my project.
What I want to do is a simple authentication using passport as this tutorial suggests: https://docs.nestjs.com/techniques/authentication
I followed this tutorial all along and at first it worked. Later I decided to use the users E-Mail and password as authentication instead of a username. So I changed my variable names and parameters in the authentication process to email and that was the point where everything broke apart. Am I missing something here?
auth.module.ts
import {Module} from '#nestjs/common';
import {UsersModule} from "../users/users.module";
import {AuthService} from "./services/auth.service";
import {PassportModule} from "#nestjs/passport";
import {LocalStrategy} from "./strategies/local.strategy";
import {AuthController} from "./controllers/auth.controller";
import {JwtModule} from "#nestjs/jwt";
import {jwtConstants} from "./constants";
import {JwtStrategy} from "./strategies/jwt.strategy";
import {EncryptionModule} from "../encryption/encryption.module";
#Module({
imports: [
UsersModule,
EncryptionModule,
PassportModule.register({defaultStrategy: 'jwt'}),
JwtModule.register({
secret: jwtConstants.secret,
signOptions: {
expiresIn: '30s'
}
})
],
providers: [
AuthService,
LocalStrategy,
JwtStrategy
],
controllers: [
AuthController
]
})
export class AuthModule {
}
controllers/auth.controller.ts
import {Controller, Get, Post, Request, UseGuards} from '#nestjs/common';
import {AuthService} from "../services/auth.service";
import {JwtAuthGuard} from "../guards/jwt-auth.guard";
import {LocalAuthGuard} from "../guards/local-auth.guard";
#Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {
}
#UseGuards(LocalAuthGuard)
#Post('login')
login(#Request() req) {
return this.authService.login(req.user);
}
#UseGuards(JwtAuthGuard)
#Get('profile')
getProfile(#Request() req) {
return req.user;
}
}
services/auth.service.ts
import {Injectable} from '#nestjs/common';
import {UsersService} from "../../users/services/users.service";
import {User} from "../../users/interfaces/user.interface";
import {JwtService} from "#nestjs/jwt";
import {JwtPayloadDto} from "../models/jwt-payload.dto";
import {EncryptionService} from "../../encryption/services/encryption.service";
#Injectable()
export class AuthService {
constructor(private usersService: UsersService,
private jwtService: JwtService,
private encryptionService: EncryptionService) {
}
async validateUser(email: string, pass: string): Promise<User | undefined> {
/**
* The findOne-method sends a database query
* to my mongodb via mongoose.
* I don't think it's necessary to post the UserService here, is it?
*/
const user: User = await this.usersService.findOne(email);
return this.encryptionService.compare(pass, user.password).then((result) => {
if (result) {
return user;
}
return undefined;
});
}
async login(user: User) {
const payload: JwtPayloadDto = {
email: user.email,
sub: user.id
}
return {
accessToken: this.jwtService.sign(payload)
};
}
}
strategies/local.strategy.ts
import {Injectable, UnauthorizedException} from "#nestjs/common";
import {PassportStrategy} from "#nestjs/passport";
import {Strategy} from "passport-local";
import {AuthService} from "../services/auth.service";
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super();
}
async validate(email: string, password: string): Promise<any> {
const user = await this.authService.validateUser(email, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
guards/local-auth.guard.ts
import {Injectable} from "#nestjs/common";
import {AuthGuard} from "#nestjs/passport";
#Injectable()
export class LocalAuthGuard extends AuthGuard('local') {
}
According to this question I found out that the validate-methods signature has to have the same parameter names as the request payloads keys.
For debugging purposes I have put a console.log()-call on the first line of my validate-method in the strategies/local.strategy.ts but it seems as it does not get called at all.
Thanks for any answer in advance.
Have a good one!

for me, when create LocalStrategy, I passed {usernameField: 'email'} to ParentClass.
If you want to check user authenticate with custom column like 'email', try pass it.
my user.entity.ts:
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column({ unique: true })
email: string;
#Column()
name: string;
}
my local.strategy.ts:
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({ usernameField: 'email' });
}
async validate(email: string, password: string): Promise<User> {
console.log(email, password); // it works
}
}

Well, I solved it myself. 5 hours of debugging wasted!
Turned out that somehow my Postman did not send the Content-Type header with the request. Restarting Postman fixed it.

Related

NestJS Authentication - Passport strategy, JWT creation problem

I'm following the official NestJS documentation. Currently, I'm trying to implement the authentication step with Passport strategy. I did every step, as the documentation says, but I got stuck where I need to generate the JWT with the jwtService.sign() method.
The error, that I'm getting is:
ERROR [ExceptionsHandler] secretOrPrivateKey must have a value`.
Here are the code snippets:
AuthModule:
#Module({
imports: [
UserModule,
PassportModule,
User,
TypeOrmModule.forFeature([User]),
JwtModule.register({
secret: 'somerandomsecret',
signOptions: { expiresIn: '60s' }
})
],
providers: [AuthService, LocalStrategy, UserService],
exports: [AuthService]
})
export class AuthModule {}
AuthService
#Injectable()
export class AuthService {
constructor(
private userService: UserService,
private jwtService: JwtService
){}
async validateUser(email: string, pass: string): Promise<any> {
const user = await this.userService.findByEmail(email);
const isMatch = await comparePasswords(pass, user.password);
if( user && isMatch) {
const { password, ...result } = user;
return result;
}
return null;
}
async signIn(user: any) {
const payload = { username: user.email, sub: user.id };
return this.jwtService.sign(payload)
}
}
And inside the User controller, I'm calling the method signIn from the AuthService.
UserController
import {
Body,
Controller,
Post,
HttpException,
HttpStatus,
Request,
UseGuards,
Bind,
} from "#nestjs/common";
import { UserService } from "./user.service";
import { SignUpDataValidation } from "./user.validation";
import { hashPassword } from "../../utils/hash-password";
import { AuthGuard } from '#nestjs/passport';
import { AuthService } from '../auth/auth.servers';
import { LocalAuthGuard } from '../auth/local-auth.guard';
#Controller("user")
export class UserController {
constructor(
private userService: UserService,
private authService: AuthService
) {}
#UseGuards(LocalAuthGuard)
#Post("/signin")
#Bind(Request())
async signIn(req) {
return this.authService.signIn(req.user)
}
}
UserModule
import { Module } from "#nestjs/common";
import { UserController } from "./user.controller";
import { UserService } from "./user.service";
import { TypeOrmModule } from "#nestjs/typeorm";
import { User } from "src/modules/user/user.entity";
import { AuthService } from '../auth/auth.servers';
import { JwtService } from '#nestjs/jwt';
#Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UserController],
providers: [UserService, AuthService, JwtService],
})
export class UserModule {}
LocalStrategy
import { Injectable, UnauthorizedException } from '#nestjs/common';
import { PassportStrategy } from '#nestjs/passport';
import { Strategy } from 'passport-local';
import { AuthService } from './auth.servers';
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({usernameField: 'email'});
}
async validate(email: string, password: string): Promise<any> {
const user = await this.authService.validateUser(email,
password);
if(!user) {
throw new UnauthorizedException()
}
return user;
}
}
As you can see, I'm also using Guards, but I'm not going to send you that code to avoid confusion.
So, can anybody tell me, why I'm getting this ERROR? Am I missing something?
Here is the link to the particular NestJS documentation page with a feature that I'm trying to implement: https://docs.nestjs.com/security/authentication

Nestjs - How to implement a ldap strategy?

I have a users REST API built with Nestjs, it connects to a mongo atlas db and fetchs the users correctly, now i want to add a ldap verification, right now i have a docker container running ldap. I've followed the documentation for using passport and
paspport strategies but i haven't figured out how to implement a ldap strategy yet. How to implement the ldap strategy using guards and routing from a users module?
I have an auth folder like this
//auth.module.ts
import { Module } from '#nestjs/common';
import { PassportModule } from '#nestjs/passport';
import { UsersModule } from 'src/users/users.module';
import { AuthService } from './auth.service';
import { LdapStrategy } from './ldap.strategy';
#Module({
providers: [AuthService, LdapStrategy],
imports: [UsersModule, PassportModule],
})
export class AuthModule {}
//ldap.strategy.ts
import * as Strategy from 'passport-ldapauth';
import { Injectable, UnauthorizedException } from '#nestjs/common';
import { PassportStrategy } from '#nestjs/passport';
import { AuthService } from './auth.service';
const OPTS = {
server: {
url: 'ldap://localhost:389',
bindDN: 'cn=admin,dc=gamestack,dc=unal,dc=edu,dc=co',
bindCredentials: 'admin',
searchBase: 'ou=sa',
searchFilter: '(uid={{username}})',
},
};
#Injectable()
export class LdapStrategy extends PassportStrategy(Strategy, 'ldapauth') {
constructor(private authService: AuthService) {
super(OPTS);
}
async validate(username: string, password: string): Promise<any> {
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
ldap-auth.guard.ts
import { Injectable } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class LdapAuthGuard extends AuthGuard('ldapauth') {}

nestjsx/crud + TypeORM: patch and post results in empty request

I'm trying to get into nestjs by creating a simple REST Api with TypeORM and the crud library. So far I have created a working role based authentication, but I'm running into a strange problem. I used the crud library to create a simple controller for the User entity. The GET-requests are working without any problems. But I can't POST to create a new user, neither can I use PATCH to update one. I think it might just be a very stupid mistake by me, but as I did not write much code, I can't find any differences to the examples in the doc.
When I try to patch a property, it just responds me with the original user object, no changes made (It's like I send an empty request).
When I try to post a new user, the response is the following error message:
{
"statusCode": 400,
"error": "Bad Request",
"message": "Empty data. Nothing to save."
}
It might have something to do with validation..
This is my user controller:
import { Controller, UseGuards } from '#nestjs/common';
import { UserService } from './user.service';
import { User } from './user.entity';
import { AuthGuard } from '#nestjs/passport';
import { ApiTags, ApiSecurity } from '#nestjs/swagger';
import { RolesGuard } from 'src/auth/role.guard';
import { Roles } from './roles.decorator';
import { Crud, CrudController } from '#nestjsx/crud';
#UseGuards(AuthGuard('jwt'), RolesGuard)
#Crud({
model: {
type: User
},
routes: {
exclude: ['createManyBase', 'replaceOneBase'],
},
//validation: false,
})
#Roles('admin')
#ApiSecurity('bearer')
#ApiTags('user')
#Controller('user')
export class UserController implements CrudController<User> {
constructor(public service: UserService) {}
}
This is my user service:
import { Injectable, Body, NotFoundException } from '#nestjs/common';
import { CreateUserDTO } from './dto/create-user.dto';
import { User } from './user.entity';
import { GetUsersFilterDto } from './dto/get-users-filter.dto';
import { InjectRepository } from '#nestjs/typeorm';
import { UserRepository } from './user.repository';
import { Role } from './role.entity';
import { TypeOrmCrudService } from '#nestjsx/crud-typeorm';
#Injectable()
export class UserService extends TypeOrmCrudService<User> {
constructor(
#InjectRepository(User) user,
private userRepository: UserRepository
) {
super(user);
}
async getUserByName(username: string): Promise<User>{
const found = await this.userRepository.findOne({
where: {
username: username,
},
relations: ["roles"]
});
if (!found){
throw new NotFoundException('User "${username}" not found!');
}
return found;
}
async getUserById(id: number): Promise<User>{
const found = await this.userRepository.findOne(id, {relations: ["roles"] });
if (!found){
throw new NotFoundException('User with "${id}" not found');
}
return found;
}
async matchRoles(roles: string[], userroles: Role[]){
let match = false;
console.log(userroles)
userroles.forEach(r => {
if (roles.indexOf('r.name')){
match = true;
}
})
return match;
}
}
This is the entity:
import { Entity, Column, PrimaryGeneratedColumn, ManyToMany, JoinTable, BeforeInsert, Unique } from 'typeorm';
import { Role } from './role.entity';
import * as bcrypt from 'bcryptjs';
import { Exclude } from 'class-transformer';
import { ApiProperty } from '#nestjs/swagger';
#Entity('auth_user')
#Unique(['username'])
export class User {
#PrimaryGeneratedColumn()
id: number;
#ApiProperty()
#Column({ length: 30 })
username: string;
#ApiProperty()
#Column()
firstName: string;
#ApiProperty()
#Column()
lastName: string;
#ApiProperty()
#Column()
email: string;
#BeforeInsert()
async hashPassword() {
this.password = await bcrypt.hash(this.password, 10);
}
#ApiProperty()
#Column()//({select: false})
#Exclude()
password: string;
#ApiProperty()
#Column({ default: true })
isActive: boolean;
#ManyToMany(
type => Role,
role => role.users,
{ cascade: true },
)
#JoinTable()
roles?: Role[];
}
Any hints are appreciated
As it turned out, it was the validation. Crud already has validation activated and I had this in the main.ts:
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true}));
So it was validated twice, what somehow led to an empty body in the request. I removed this and now I'm able to post/patch/put.

Applying Middleware-like mechanism to Resolvers' Queries and Mutations

I'm building a GraphQL API using Nest framework and I'm trying to implement 3rd party express middlewares (express-rate-limit and express-slow-down) into some queries and mutations.
The problem is all graphql mutations and queries use the same endpoint, so I can't explicitly tell to which query or mutations shall the middleware be applied, because you can only do that using route's path (which is the same across the API).
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '#nestjs/common'
import * as rateLimit from 'express-rate-limit'
import * as RedisStore from 'rate-limit-redis'
import { RedisClient } from 'redis'
#Module({
providers: [],
exports: [],
})
export default class SecurityModule implements NestModule
{
constructor(protected readonly redisClient: RedisClient)
{
}
configure(consumer: MiddlewareConsumer)
{
consumer.apply(
new rateLimit({
max: 300,
windowMs: 15 * 60 * 1000,
store: new RedisStore({ client: this.redisClient }),
})).forRoutes({ path: '/graphql', method: RequestMethod.ALL }) // this would apply the middleware to all queries and mutations
}
}
So I tried using both guards and interceptors for that purpose, but failed miserably.
It's a fail for an obvious reason.
The Error: Can't set headers after they are sent is thrown.
/* !!! My Interceptor would like quite identical */
import { ExecutionContext, Injectable, CanActivate } from '#nestjs/common'
import * as speedLimit from 'express-slow-down'
import { Request, Response } from 'express'
#Injectable()
export default class SpeedLimitGuard implements CanActivate
{
constructor(
protected readonly options: speedLimit.Options,
) {
}
async canActivate(context: ExecutionContext): Promise<boolean> {
const { req, res }: { req: Request, res: Response } = context.getArgs()[2]
speedLimit({ ...this.options })(req, res, req.next)
return true
}
}
import { NestInterceptor, ExecutionContext, Injectable, INestApplication, INestExpressApplication } from '#nestjs/common'
import { Observable } from 'rxjs'
import * as speedLimit from 'express-slow-down'
// import { Request, Response } from 'express'
import { ApplicationReferenceHost } from '#nestjs/core'
import { RedisClient } from 'redis'
import * as RedisStore from 'rate-limit-redis'
#Injectable()
export default class SpeedLimitInterceptor implements NestInterceptor
{
constructor(private readonly appRefHost: ApplicationReferenceHost,
private readonly redisClient: RedisClient, )
{}
intercept<T>(context: ExecutionContext, call$: Observable<T>): Observable<T>
{
// const { req: request, res: response }: { req: Request, res: Response } = context.getArgs()[2]
const httpServer = this.appRefHost.applicationRef
const app: INestApplication & INestExpressApplication = httpServer.getInstance()
app.use(speedLimit({
delayAfter: 1,
store: new RedisStore({
prefix: 'test_',
client: this.redisClient,
}),
}))
app.use((req, res, next) => {
console.log('is middleware triggered', { req, res })
next()
})
return call$
}
}
Is there any way to apply a 3rd party express middleware to a GraphQL Mutation/Query explicitly?
So from the bottom, guards are working, because I'm the living human bean that can prove it:
#Query('getHome')
#UseGuards(GraphqlGuard)
async findOneById(#Args('id') id: string): Promise<HomeEntity> {
return await this.homeService.findOneById(id);
}
and it's just working.
This is GraphqlGuard.ts
import {ExecutionContext, Injectable} from '#nestjs/common';
import {GqlExecutionContext} from '#nestjs/graphql';
import {AuthGuard} from '#nestjs/passport';
import {ExecutionContextHost} from '#nestjs/core/helpers/execution-context.host';
import {Observable} from 'rxjs';
#Injectable()
export class GraphqlGuard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
const ctx = GqlExecutionContext.create(context);
const {req} = ctx.getContext();
return super.canActivate(new ExecutionContextHost([req]));
}
}
But to live with context, you have to make it works for you, so, wherever you're passing graphql config, there is an context callback, and for me it looks like this:
context: (context) => {
let req = context.req;
if (context.connection) {
req = context.connection.context.req;
}
return {req};
}
I'm checking here connection for context from websocket. Im using global interceptors so, they're working like a charm. But you still can use #UseInterceptors(SomeInterceptor) decorator and it also works. And btw Middlewares, at the end, I doesn't need any of them guards, pipes, validators and interceptors for me was quite enough.
Regards.

Autoinjected API

i am developing an application using Angular2 (actually, ionic framework). Application itself contains a login form that should work via API.
Structure:
form.ts
Contains logic related to login form
import {Component, Injectable} from '#angular/core';
import { Http } from '#angular/http';
import {User} from '../../../base/user';
import {API} from '../../../base/API';
#Component({
templateUrl: 'build/pages/account/loginForm/form.html',
selector: 'login-form',
providers: [Http, API]
})
export class AccountForm {
constructor() {
...
}
submitLogin($event) {
var username = this.username.value;
var password = this.password.value;
this.user = new User(username, password);
this.user.authenticate()
}
}
user.ts
Class that contains some user information (username, password hash)
import { Inject } from '#angular/core';
import { Hash } from "./hash";
import { API } from "./API";
export class User {
public username: string;
public password: string;
public pwdhash: string;
constructor(
username: string,
password: string,
private api: API ///// problem is here
) {
let hash = new Hash();
this.username = username;
this.password = password;
this.pwdhash = hash.hash(username, password);
}
authenticate() {
var promise = this.api.loginUser({username: this.username, pwdhash: this.pwdhash});
console.log(promise);
}
}
API.ts
Class with API request to the backed
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Injectable, Inject } from '#angular/core';
import { Hash } from './hash';
#Injectable()
export class API {
private http: Http;
constructor(#Inject(Http) http: Http) {
}
loginUser (user) {
return this.http.post(url, body, options);
}
}
What i want to do is use API class in a User.authenticate() call, but i don't want to pass it as a parameter to the User.constructor(), as this is a bad style.
Another option is to create a User.API member in a constructor, which looks ugly too.
Looks like i need just a function that makes API requests. However, i am not sure this is a good idea for Object-Oriented Programming.
Is there any way to inject API into User class without creating a new API object of this class?
Thanks