How to make custom response in pipe of nestjs - javascript

I made HttpExceptionFilter as below in nestjs.
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
} from '#nestjs/common';
import { Response } from 'express';
#Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception, host: ArgumentsHost) {
const context = host.switchToHttp();
const response = context.getResponse<Response>();
const status = (exception.getStatus && exception.getStatus()) || 500;
response.status(status).json({
code: status,
success: false,
});
}
}
And I put it into app.module to use it globally.
#Module({
imports: [
],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
So far, it works very well except pipe of nestjs.
I made a pipe and made it with #UsePipes in other controller.
This is my pipe code.
import { ArgumentMetadata, Injectable, PipeTransform } from '#nestjs/common';
#Injectable()
export class SplitEmailPipe implements PipeTransform<string, string> {
transform(value: any, metadata: ArgumentMetadata): any {
let email = '';
try {
email = value.split('#')[1];
} catch (err) {
throw new Error(err);
}
return { email };
}
}
And I put that pipe using #UsePipes.
Pipe works well in this case.
#Post('/')
#UsePipes(new SplitEmailPipe())
public async signIn(
#Res() res,
#Body() signInDto: SignInDto,
) {
... do something
}
But the problem is HttpExceptionFilter doesn't work. It response by default response of nestjs.
Could you give me some advice for this problem?

That happens because you're not throwing an error from type HttpException, to fix this issue you should to replace :
import { ArgumentMetadata, Injectable, PipeTransform } from '#nestjs/common';
#Injectable()
export class SplitEmailPipe implements PipeTransform<string, string> {
transform(value: any, metadata: ArgumentMetadata): any {
let email = '';
try {
email = value.split('#')[1];
} catch (err) {
throw new BadRequestException(err);
}
return { email };
}
}

Related

Valid JWT is invalid for nestJS guard

I'm passing a JWT from my client application (nextJS with nextAuth) via credentials header to my backend nestJS application (which is using graphQL). In my nestJS backend application I'm trying to implement an auth guard, so I extract the JWT with a custom function in my jwt.strategy.ts
But the JwtStrategy is not accepting my valid signed token. To prove that the JWT is valid, I put some console output for the token. But the validate() function is never called. I do not understand why, as the token can be validated with jwt.verify:
This is my output - it gets decoded by the jwt.verify():
JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJJZCI6MTIzLCJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaXNBZG1pbiI6dHJ1ZX0sImlhdCI6MTYwOTY3NTc4Nn0.LQy4QSesxJR91PyGGb_0mGZjpw9hlC4q7elIDs2CkLo
Secret: uGEFpuMDDdDQA3vCtZXPKgBYAriWWGrk
Decoded: {
user: { userId: 123, username: 'username', isAdmin: true },
iat: 1609675786
}
I don't see, what I am missing and I even do not see how to debug it as there is no output in my jwt.strategy.ts and the validate-function is not called at all.
jwt.strategy.ts
import jwt from 'jsonwebtoken'
// import { JwtService } from '#nestjs/jwt'
import { Strategy } from 'passport-jwt'
import { PassportStrategy } from '#nestjs/passport'
import { Injectable } from '#nestjs/common'
import cookie from 'cookie'
import { getConfig } from '#myapp/config'
const { secret } = getConfig()
const parseCookie = (cookies) => cookie.parse(cookies || '')
const cookieExtractor = async (req) => {
let token = null
if (req?.headers?.cookie) {
token = parseCookie(req.headers.cookie)['next-auth.session-token']
}
// output as shown above
console.log('JWT:', token)
console.log('Secret:', secret)
const decoded = await jwt.verify(token, secret, { algorithms: ['HS256'] })
console.log('Decoded: ', decoded)
return token
}
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: cookieExtractor,
ignoreExpiration: true,
secretOrKey: secret
})
}
async validate(payload: any) {
console.log('payload:', payload) // is never called
return { userId: payload.sub, username: payload.username }
}
}
jwt-auth.guard.ts
import { Injectable, ExecutionContext } from '#nestjs/common'
import { AuthGuard } from '#nestjs/passport'
import { GqlExecutionContext } from '#nestjs/graphql'
#Injectable()
export class GqlAuthGuard extends AuthGuard('jwt') {
getRequest(context: GqlExecutionContext) {
const ctx = GqlExecutionContext.create(context)
return ctx.getContext().req
}
}
The guard is used in this resolver:
editor.resolver.ts
import { Query, Resolver } from '#nestjs/graphql'
import { UseGuards } from '#nestjs/common'
import { GqlAuthGuard } from '../auth/jwt-auth.guard'
#Resolver('Editor')
export class EditorResolvers {
constructor(private readonly editorService: EditorService) {}
#UseGuards(GqlAuthGuard)
#Query(() => [File])
async getFiles() {
return this.editorService.getFiles()
}
}
auth.module.ts
import { Module } from '#nestjs/common'
import { AuthController } from './auth.controller'
import { AuthService } from './auth.service'
import { PassportModule } from '#nestjs/passport'
import { LocalStrategy } from './local.strategy'
import { JwtStrategy } from './jwt.strategy'
import { UsersModule } from '../users/users.module'
import { JwtModule } from '#nestjs/jwt'
import { getConfig } from '#myApp/config'
const { secret } = getConfig()
#Module({
imports: [
UsersModule,
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.register({
secret,
verifyOptions: { algorithms: ['HS256'] },
signOptions: { expiresIn: '1d' }
})
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy, LocalStrategy],
exports: [AuthService]
})
export class AuthModule {}
The token is created on server side (nextJS api page) with:
const encode = async ({ secret, token }) => jwt.sign(token, secret, { algorithm: 'HS256' })
I see 2 differences from nestJS docs examples from your jwt.strategy.ts file that you can change and give it a try..
https://docs.nestjs.com/security/authentication#implementing-passport-jwt
sync extractor and not async
By default passport-jwt extractor we can see that is an sync and not async, so you can try change your extractor and remove the async, or to add await when calling it.
https://github.com/mikenicholson/passport-jwt/blob/master/lib/extract_jwt.js ,
look for fromAuthHeaderAsBearerToken function.
so or change your
const cookieExtractor = async (req) => {
to
const cookieExtractor = (req) => {
OR - add await when you call it
jwtFromRequest: await cookieExtractor(),
call the extractor and not just pass it
by the docs example in JwtStrategy constructor they calling the extractor and not passing it like you do
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
so try to call it in your JwtStrategy constructor
jwtFromRequest: cookieExtractor(), // (again - take care of sync / async)

Error: Expected no open requests, found 1 (Angular)

I am trying to create a test case for a service in angular6. The service has a bunch of different http request methods (get, put, post etc) and within them an API call is made which fetches the appropriate response. I'm trying to create the test cases where a mock http request is made and a response is returned. However, I have followed a Tutorial which apparently helps me do exactly what I want.
However, when I run the test case for the service it gives me the following error (I've censored the URL in GET for privacy purposes:
Error: Expected no open requests, found 1: GET https://staging.xxxxxxxxxx.co.uk/rest/v11_1/oauth2/token
at HttpClientTestingBackend.push../node_modules/#angular/common/fesm5/http/testing.js.HttpClientTestingBackend.verify (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/common/fesm5/http/testing.js:326:1)
at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/src/app/Services/adapter.service.spec.ts:22:13)
at TestBed.push../node_modules/#angular/core/fesm5/testing.js.TestBed.execute (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/fesm5/testing.js:1073:1)
at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/fesm5/testing.js:1224:29)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:288:1)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:387:1)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:138:1)
at runInTestZone (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:509:1)
at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:524:1)
I've tried browsing through This solution as well as This one, but to no avail.
Here is the code for my service:
import { Injectable } from '#angular/core';
import { environment } from '../../environments/environment';
import {
HttpHeaders,
HttpClient,
HttpParams,
} from '#angular/common/http';
import { Request, RequestOptions, Headers } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { JwtService } from './jwt.service';
const API_URL = environment.api.host;
#Injectable({
providedIn: 'root'
})
export class AdapterService {
constructor(private http: HttpClient, private jwtService: JwtService) {}
private formatErrors(self: AdapterService) {
return (res: Response) => {
return Observable.throw(res);
};
}
private requestHeaders(path: string) {
let headers;
if (path !== 'oauth2/token') {
headers = new HttpHeaders({
'Accept': 'application/json',
'Oauth-Token': this.jwtService.getToken()
})
}
return headers;
}
get(path: string, params: HttpParams = new HttpParams()): Observable < any > {
let headers = this.requestHeaders(path);
return this.http.get(`${API_URL}${path}`, { headers })
.catch(catchError(this.formatErrors(this)));
}
put(path: string, body: Object = {}): Observable < any > {
return this.http.put(
`${API_URL}${path}`,
JSON.stringify(body),
).catch(catchError(this.formatErrors(this)));
}
post(path: string, body: Object = {}): Observable < any > {
return this.http.post(
`${API_URL}${path}`,
JSON.stringify(body),
).catch(catchError(this.formatErrors(this)));
}
delete(path): Observable < any > {
return this.http.delete(
`${API_URL}${path}`,
).catch(catchError(this.formatErrors(this)));
}
}
The Test Case:
import { TestBed, async, inject } from '#angular/core/testing';
import { HttpClientModule, HttpRequest, HttpParams } from '#angular/common/http';
import { HttpClientTestingModule, HttpTestingController } from '#angular/common/http/testing';
import { AdapterService } from './adapter.service';
describe('AdapterService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientModule,
HttpClientTestingModule
],
providers: [
AdapterService
]
});
});
afterEach(inject([HttpTestingController], (backend: HttpTestingController) => {
backend.verify();
}));
it('should send a valid get request for token', async(inject([AdapterService, HttpTestingController],
(service: AdapterService, backend: HttpTestingController) => {
service.get('oauth2/token').subscribe((next)=>{
expect(next).toBeDefined();
});
})));
// it('')
});
SOLVED I forgot to add an expectOne request for the API call within the test case:
backend.expectOne( API_URL + 'oauth2/token').flush(null, { status: 200, statusText:'Ok' });
A very naive observation, apologies for the inconvenience.

Not receiving Products from function in Angular

I am working through the tutorial files in the book Pro Angular for a Store Front application. I am currently working on an administration section and am getting an error saying "Failed to load resources, cannot connect to the server". This is my rest.datasource.ts file:
import { Injectable } from "#angular/core";
import { Http, Request, RequestMethod } from "#angular/http";
import { Observable } from "rxjs/Observable";
import { Product } from "./product.model";
import { Cart } from "./cart.model";
import { Order } from "./order.model";
import "rxjs/add/operator/map";
const PROTOCOL = "http";
const PORT = 3000;
#Injectable()
export class RestDataSource {
baseUrl: string;
auth_token: string;
constructor(private http: Http) {
this.baseUrl = `${PROTOCOL}://${location.hostname}:${PORT}/`;
}
authenticate(user: string, pass: string): Observable<boolean> {
return this.http.request(new Request({
method: RequestMethod.Post,
url: this.baseUrl + "login",
body: { name: user, password: pass }
})).map(response => {
let r = response.json();
this.auth_token = r.success ? r.token : null;
return r.success;
});
}
getProducts(): Observable<Product[]> {
return this.sendRequest(RequestMethod.Get, "products");
}
saveProduct(product: Product): Observable<Product> {
return this.sendRequest(RequestMethod.Post, "products",
product, true);
}
updateProduct(product): Observable<Product> {
return this.sendRequest(RequestMethod.Put,
`products/${product.id}`, product, true);
}
deleteProduct(id: number): Observable<Product> {
return this.sendRequest(RequestMethod.Delete,
`products/${id}`, null, true);
}
getOrders(): Observable<Order[]> {
return this.sendRequest(RequestMethod.Get,
"orders", null, true);
}
deleteOrder(id: number): Observable<Order> {
return this.sendRequest(RequestMethod.Delete,
`orders/${id}`, null, true);
}
updateOrder(order: Order): Observable<Order> {
return this.sendRequest(RequestMethod.Put,
`orders/${order.id}`, order, true);
}
saveOrder(order: Order): Observable<Order> {
return this.sendRequest(RequestMethod.Post,
"orders", order);
}
private sendRequest(verb: RequestMethod,
url: string, body?: Product | Order, auth: boolean = false)
: Observable<Product | Product[] | Order | Order[]> {
let request = new Request({
method: verb,
url: this.baseUrl + url,
body: body
});
if (auth && this.auth_token != null) {
request.headers.set("Authorization", `Bearer<${this.auth_token}>`);
}
return this.http.request(request).map(response => response.json());
}
}
and here is my static.datasource.ts file:
import { Injectable } from "#angular/core";
import { Product } from "./product.model";
import { Observable } from "rxjs/Observable";
import "rxjs/add/observable/from";
import { Order } from "./order.model";
#Injectable()
export class StaticDataSource {
//Populate product fields with Inventory information
private products: Product[] = [
//products omitted for brevity
];
getProducts(): Observable<Product[]> {
return Observable.from([this.products]);
}
saveOrder(order: Order): Observable<Order> {
console.log(JSON.stringify(order));
return Observable.from([order]);
}
}
I am following the tutorial exactly but for some reason the site falls apart now that I am on Chapter 9 creating the admin section. Any help or pointers would be greatly appreciated.

Trying to use tokenNotExpired from angular2-jwt but getting error in modules

I am trying to use tokenNotExpired of angular2-jwt for check if user is logged in or not. But when i implement this I get below error in cli:-
ERROR in node_modules/angular2-jwt/angular2-jwt.d.ts(3,10): error TS2305: Module '"d:/Visual Studio/asp.net/mean_blog/client/node_modules/rxjs/Observable"' has no exported member 'Observable'.
node_modules/rxjs/Observable.d.ts(1,15): error TS2307: Cannot find module 'rxjs-compat/Observable'.
I have install angular2-jwt using "npm install angular2-jwt#latest --save" commmand.
Below is the auth.service.ts code:-
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions } from '#angular/http';
import { map, take } from 'rxjs/operators';
import { tokenNotExpired } from 'angular2-jwt';
#Injectable({
providedIn: 'root'
})
export class AuthService {
authToken;
user;
options;
constructor(private http: Http) { }
createAuthenticationHeaders(){
this.loadToken();
this.options = new RequestOptions({
headers:new Headers({
'Content-Type':'application/json',
'authorization':this.authToken
})
})
}
loadToken(){
this.authToken = localStorage.getItem('token');
}
registerUser(user) {
return this.http.post('/api/auth/register', user).pipe(map(res => res.json()));
}
checkUsername(username) {
return this.http.get('/api/auth/checkUsername/' + username).pipe(map(res => res.json()));
}
checkEMail(email) {
return this.http.get('/api/auth/checkEmail/' + email).pipe(map(res => res.json()));
}
login(user){
return this.http.post('/api/auth/login',user).pipe(map(res=>res.json()));
}
logout(){
this.authToken = null;
this.user = null;
localStorage.clear();
}
storeUserData(token, user){
localStorage.setItem('token',token);
localStorage.setItem('user',JSON.stringify(user));
this.authToken = token;
this.user = user
}
getProfile(){
this.createAuthenticationHeaders();
return this.http.get('/api/auth/profile',this.options).pipe(map(res=>res.json()));
}
loggedIn() {
return tokenNotExpired();
}
}
I have also check the dependency in package.json that is also added correctly.
I don't know from where this error is occured.
Some function of rxjs was deprecated in angular 6, so I use rxjs-compat for backward compatibility. It solve the issue.

Value become null for this in Promise callback

I am using the following code value of this become null when i call it inside the then function here is the code. Am i doing something wrong or it is like this or there is any work around to resolve this issue
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { CanActivate, Router } from '#angular/router';
import { AuthService } from '../services/auth.service';
import { WebAPISettings } from '../services/webapisettings.service';
#Injectable()
export class LoginService {
//_ngWEBAPISettings: WebAPISettings;
//_authService: AuthService;
constructor(private http: Http, private ngWEBAPISettings: WebAPISettings, private authService: AuthService) {
//this._ngWEBAPISettings = ngWEBAPISettings;
//this._authService = authService;
}
public login(username: string, password: string): Promise<any> {
let data = "grant_type=password&username=" + username + "&password=" + password;
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
let options = new RequestOptions({ headers: headers });
try {
debugger;
return this.http.post(this.ngWEBAPISettings.apiServiceBaseUri + "token", data, options)
.toPromise()
.then(function (res: Response) {
debugger;
let body = res.json();
//let _authService: AuthService = new AuthService();
this.authService.fillAuthDataFromLogin(body);
//this.router.navigate(['/Home']);
return body.data || {};
})
.catch(this.handleError);
}
catch (error) {
console.log(error);
}
}
private extractData() {
}
private handleError(error: any) {
debugger;
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
}
and i am debugging it in the chrome here is the screenshot please help me in fixing it.
after using the arrow function same thing check the screen shot
one thing to mention i am using Angular2 RC4.
You could use an arrow function to be able to use the lexical this:
return this.http.post(this.ngWEBAPISettings.apiServiceBaseUri + "token", data, options)
.toPromise()
.then((res: Response) => { // <-----
(...)
});
This way, this will correspond to the instance of the LoginService service.
See this doc for more details:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Categories

Resources