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
Related
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
I've managed to get JWT authentication in my nestJS application.
Now I want to implement the role guard and have therefore to check the role of the authenticated user.
Therefore, I thought of requesting the respective user role from the database. But this call is async and this is not doable within the guard.
My question is:
How can I get the user role information within the Guard?
I could put the information in the JWT token, but this seems not right to me, or am I wrong?
Here, Implementing Passport JWT you can put your findUser in the validate function that is async. And then create a decorator to return the user Auth JWT in decorator in NESTJS
So you need to do some things like this
//jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '#nestjs/passport';
import { Injectable } from '#nestjs/common';
import { jwtConstants } from './constants';
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: jwtConstants.secret,
});
}
async validate(payload: any) {
// Your JWT payload
// Insert here the findOne from you're BDD
return { userId: payload.sub, username: payload.username };
}
}
And then
//user.decorator.ts
import { createParamDecorator, ExecutionContext } from '#nestjs/common';
export const User = createParamDecorator((data: any, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
});
And in your controller juste use
//user.controller.ts
import { User } from './user.decorator';
#Get()
async getUser(#User() user) {
//console.log(user);
}
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') {}
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.
i am currently implementing an angular2 example application with spring boot as backend. I am having some problems with the frontend auth guard mechanism and observables.
I am trying to achieve:
when someone enters a protected route the auth guard should check if a user
is already set in the auth service variable
if it is not set then a http request should be issued to check if a session is available
the service method should return a true/false value (asynchronously because of the possible http request)
if service returns false the auth guard should redirect to login page
auth guard should return true/false so the route can either be activated or not
My code currently looks like this (i am using RC5 btw.):
Auth Guard
import {Injectable} from "#angular/core";
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router} from "#angular/router";
import {Observable, Subject} from "rxjs/Rx";
import {AuthService} from "./auth.service";
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
var authenticated = this.authService.isAuthenticated();
var subject = new Subject<boolean>();
authenticated.subscribe(
(res) => {
console.log("onNext guard: "+res);
if(!res && state.url !== '/signin') {
console.log("redirecting to signin")
this.router.navigate(['/signin']);
}
subject.next(res);
});
return subject.asObservable();
}
}
Auth Service
import {Injectable} from "#angular/core";
import {User} from "./user.interface";
import {Router} from "#angular/router";
import {Http, Response, Headers} from "#angular/http";
import {environment} from "../environments/environment";
import {Observable, Observer, Subject} from "rxjs/Rx";
#Injectable()
export class AuthService {
private authenticatedUser : User;
constructor(private router: Router, private http: Http) {}
signupUser(user: User) {
}
logout() {
//do logout stuff
this.router.navigate(['/signin']);
}
isAuthenticated() : Observable<boolean> {
var subject = new Subject<boolean>();
if (this.authenticatedUser) {
subject.next(true);
} else {
this.http.get(environment.baseUrl + '/user')
.map((res : Response) => res.json())
.subscribe(res => {
console.log("next: returning true");
this.authenticatedUser = User.ofJson(res);
subject.next(true);
}, (res) => {
console.log("next: returning false");
subject.next(false);
});
}
return subject.asObservable();
}
}
The problem is: the guard never allows the router component to activate, even though when i am logged in.
Thanks for the help!
Change
return subject.asObservable();
to
return subject.asObservable().first();
The router waits for the observable to complete. first() makes it complete after the first event.