NestJS - How to create wrapping service over jwt service (from jwt module) - jwt

Sorry for my bad english, I'm from Ukraine :)
Could you tell me how can I create my own service, that extends of Jwt service provided jwt module from npm package? I want to create my own JwtService for catch errors and isolate duplicate logic for token creation and verification. Please, help me how can I do it. Code samples attached.
import { BadRequestException, Injectable } from '#nestjs/common';
import { JwtService as NestJwtService, JwtVerifyOptions } from '#nestjs/jwt';
#Injectable()
export class OwnJwtService extends NestJwtService {
constructor() {
super({});
}
async verifyAsync<T>(token: string, options?: JwtVerifyOptions): Promise<T> {
try {
const res = await super.verifyAsync(token, options);
console.log('res', res);
return res;
} catch (error) {
// My own logic here ...
throw new BadRequestException({
error,
message: 'Error with verify provided token',
});
}
}
}
or maybe I need to inject nestjs jwt service to my own service ? example:
import { BadRequestException, Injectable } from '#nestjs/common';
import { JwtService as NestJwtService, JwtVerifyOptions } from '#nestjs/jwt';
#Injectable()
export class OwnJwtService {
constructor(private readonly jwtService: NestJwtService) {}
async verifyAsync<T>(token: string, options?: JwtVerifyOptions): Promise<T> {
try {
const res = await this.jwtService.verifyAsync(token, options);
console.log('res', res);
return res;
} catch (error) {
throw new BadRequestException({
error,
message: 'Error with verify provided token',
});
}
}
}
and
import { JwtModule as NestJwtModule } from '#nestjs/jwt';
import { ConfigModule, ConfigService } from '#nestjs/config';
import { Module } from '#nestjs/common';
import { OwnJwtService } from 'src/modules/jwt/jwt.service';
#Module({
imports: [
NestJwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
signOptions: {
expiresIn: process.env.JWT_EXPIRES_IN,
},
secret: process.env.JWT_SECRET,
secretOrPrivateKey: process.env.JWT_SECRET,
}),
inject: [ConfigService],
}),
],
providers: [OwnJwtService],
exports: [OwnJwtService],
})
export class JwtModule {}
but it doesn't work for me, and I have similar errors:
Error: Nest can't resolve dependencies of the OwnJwtService (?). Please make sure that the argument JwtService at index [0] is available in the AuthModule context.

First, notice that the JwtModule basically creates a module based on jsonwebtoken and your custom errors aren't meant to be dealt inside it.
Second, when you use registerAsync you are meant to get your ENV variables with the ConfigService as in configService.get('JWT_SECRET').
Third, your question is inefficient. The JwtModule already does everything you need. You just need to implement it. Again, just think of it as the jsonwebtoken package adapted for Nest. That's it.
On the signup, login and refreshtoken (if existing) routes you sign when you create a new token.
And in your requests middleware you verify.
One kind of a big issue with Nest is its documentation. It doesn't have everything you need. There might be more than one way to verify a route, but the most straightforward is just using Express middleware, as in a typical Express app.
To do this, you need to implement it in the AppModule like this:
#Module(...)
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer): MiddlewareConsumer | void {
consumer.apply(cookieParser(), AuthMiddleware).forRoutes('/');
}
}
In this example, I'm also registering the module cookieParser() because I send the tokens in a cookie. Other cookie modules will do, too. Both the NestModule and the MiddlewareConsumer come from #nestjs/common.
AuthMiddleware is a middleware I made using this skeleton...
export class AuthMiddleware implements NestMiddleware {
constructor(
private readonly configService: ConfigService,
private readonly jwtService: JwtService
) {}
async use(req: Request, res: Response, next: NextFunction) {
const { yourJwtToken } = req.cookies;
const isValidToken = this.jwtService.verify(
yourJwtToken,
this.configService.get('JWT_SECRET'),
);
if (!isValidToken) throw new UnauthorizedException();
// etc...
next();
}
}
Finally, what you might be asking to, is to apply the AuthGuard.
If you use the Passport ones, you need just to follow the documentation to apply them. They already throw errors if you. If you want to change it, just rewrite its methods.
You can also do it manually. Just use the console to generate a guard, and in there you can check authentication context.switchToHttp().getRequest() and return a boolean after checking the credentials and use the constructor to check the permissions if you want.
You might also skip the middleware config from above and implement the logic inside the guard if you will.
Again, I don't really think changing the JwtModule is the best idea here.

Related

keep getting internal server error with nest.js jwt

I am using Nest.js JWT to protect my resources but i keep getting internal server error when i dont provide token or the token is invalid instead of get unauthorization exception as shown in the following jwt strategy file
import { Injectable, UnauthorizedException } from "#nestjs/common";
import { PassportStrategy } from "#nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";
import { AuthService } from "../services/auth.service";
export interface JwtPayload {
user: string,
refreshToken: boolean
}
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
private authService: AuthService
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.SECURITY_KEY,
});
}
async validate(payload: JwtPayload) {
// prevent passing refresh token as access token
if (payload.refreshToken) {
throw new UnauthorizedException('Access Token Only');
}
const user = await this.authService.getUserFromJwtPayload(payload.user);
if (!user) {
throw new UnauthorizedException('Invalid User');
}
// checks if user logged out
if (!user.refreshToken) {
throw new UnauthorizedException('You have logged out');
}
return user;
}
}
Please assist me i dont know why am keeping getting internal server error, is there any place missing or there is something i have to do.
I think something is going wrong in this process:
const user = await this.authService.getUserFromJwtPayload(payload.user);
So it does not get tot the part where you check if you have a valid user or refreshToken. I think that if you remove both if statements that you will still get the internal server error.
Make sure you run the following command if you get internal server error npm install #nestjs/jwt #nestjs/passport #nestjs/passport-jwt passport passport-jwt

Inject service or class in guard in NestJS

Needs to DI Kafka client in guard:
auth.guard.ts
export class AuthGuard implements CanActivate {
private _client: ClientKafka; <----- // TODO implement nestjs DI mechanism
public async canActivate(context: ExecutionContext): Promise<boolean> {
try {
const request = context.switchToHttp().getRequest();
const authorization: string = request.get('Authorization');
...code here just send data to jwt service...
return true;
} catch (err) {
return false;
}
}
}
I use new in canActivate for creating an instance of Kafka client in auth.guard.ts. But how to inject a class in guard with #Inject? I used to create #Global module, which provides and export Kafka client class, but it's not working...
Use This in the module for globally using the guard
providers: [{provide: APP_GUARD, useClass: AuthGuard}]
As for your question about injecting a class inside a guard, you need to inject it inside the constructor of the AuthGuard class
export class AuthGuard implements CanActivate {
constructor(private clientKafka : ClientKafka){}
}
if this doesn't work, try using
constructor(#Inject(private clientKafka : ClientKafka)){}
Hope this resolves your issue :)

How to handle two calls and the loading controller in Ionic 4

I have a requirement where I have 2 API calls, and I want the first two calls to be there for the first request. And 2nd API call to be there when navigated back.
I am calling 1st API in ngOnInit webhook and 2nd API on ionViewWillEnter webhook.
The issue which I am facing is sometimes my loader doesn’t get dismissed when both of the request complete at the same time.
So the possible solution which I am thinking is that if I could call both APIs on the first load synchronously and thereafter call another API every time the back button is clicked.
NOTE: I am using loaders in my interceptor.
CODE: For interceptor
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Clone the request to add the new header.
const authReq = req.clone();
this.showLoading();
// send the newly created request
return next.handle(authReq).pipe(catchError((error) => {
if (error.status === 401) {
this._navCtrl.navigateForward('/login');
}
return throwError(error);
}), finalize( () => {
console.log('hi');
this.dismissLoading();
})
);
}
EDIT:
Code to show loader and hide loader:
async showLoading() {
return await this._loadingCtrl.create().then(a => {
a.present();
});
}
async dismissLoading() {
return await this._loadingCtrl.dismiss();
}
In my case, I will create a LoaderService to handle the Loading by myself. The special thing is I will create a flag called isShowing, so if the loading is already showing, we just need to update the loading message by calling presentLoader function again. There will be only one Loading popup show up on your screen.
In your case, I would not recommend to display the Loader in HTTP Interceptor because we cannot handle the HTTP call stack there. Just create a new function that combines all necessary API calls and show/dismiss popup when you start/finish processing the data.
import { LoadingController } from '#ionic/angular';
import { Injectable } from '#angular/core';
#Injectable()
export class LoaderService {
private loading: HTMLIonLoadingElement;
private isShowing = false;
constructor(private loadingController: LoadingController) {}
public async presentLoader(message: string): Promise<void> {
if (!this.isShowing) {
this.loading = await this.loadingController.create({
message: message
});
this.isShowing = true;
return await this.loading.present();
} else {
// If loader is showing, only change text, won't create a new loader.
this.isShowing = true;
this.loading.message = message;
}
}
public async dismissLoader(): Promise<void> {
if (this.loading && this.isShowing) {
this.isShowing = false;
await this.loading.dismiss();
}
}
}
The simple solution would be to make a function call whenever you click the bak button and inside the function you can make a API call.
Instead of linking to the back button you can use ionViewWillEnter, which is called whenever you are about to leave a page but the downside would be it is called every time view is changed regardless of the fact that only when back button is clicked.
Also you should check, is your service singleton and it creates a single instance of ionic-loader. I think in your case more than one instance of loader is being created.
Also instead of calling the loader in interceptor, you can call showLoading() in ionViewWillEnter and hideLoading() in ionViewDidEnter() of your page.
You can create a Singleton Loader Service as shown below.
This service will take care of creating only a single instance of ionic loader.
import { Injectable } from '#angular/core';
import { LoadingController } from '#ionic/angular';
#Injectable({
providedIn: 'root'
})
export class LoaderService {
private loader: HTMLIonLoadingElement;
constructor(private loadingController: LoadingController) {}
async showLoader() {
if (!this.loader) {
this.loader = await this.loadingController.create({ message: 'Loading' });
}
await this.loader.present();
}
async hideLoader() {
if (this.loader) {
await this.loader.dismiss();
this.loader = null;
}
}
}
private loading: HTMLIonLoadingElement;
constructor(public loadingController: LoadingController) { }
public async show(): Promise<void> {
return await this.loadingController.create({
message: 'Please wait...',
spinner: 'crescent'
}).then(a => {
a.present().then(() => {
console.log('presented');
});
});
}
return await this.loadingController.dismiss().then(() =>
console.log('dismissed'));
}`enter code here`

Using Custom Pipes in services in angular2

I want to call the my custom pipe inside Injectable service. I checked many threads in stackoverflow. But they talk about using custom pipes inside a component. Can u please help me here, any helpful link will be fine. Below is my custom pipe file:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({ name: 'unit' })
export class UnitPipe implements PipeTransform {
transform(val,unit, args) {
if(unit=='Metric') {
return val * 2;
}
else {
return val * 4;
}
}
}
And Iam trying to access this pipe in my service:
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { UnitPipe } from '../pipes/UnitPipe';
#Injectable()
export class SomeService {
constructor(http: Http, unitPipe: UnitPipe) {
this.http = http;
this.unitPipe = unitPipe;
}
transformUnit() {
return this.unitPipe.transform('10', 'Metric');
}
}
I have specified this in app.module.js
declarations: [UnitPipe],
providers: [UnitPipe]
And in my component.js, I am calling this service method & just checking the output in console:
import { Component, OnInit, EventEmitter, Input } from '#angular/core';
import { SomeService } from '../../services/SomeService';
#Component({
})
export class SomeClass implements OnInit {
constructor(someService: SomeService) {
this.someService = someService;
}
ngOnInit(): void {
console.log(this.someService.transformUnit());
}
}
But Iam getting below error
One more thing is, my plan is to call transformUnit method inside my service file 'SomeService', may be onload, where the function definition is present. Any thought on this?
Thank you.
Your pipe transform function expects 3 parameters:
transform(val,unit, args) {
...
}
You're providing only 2 parameters to it:
transformUnit() {
return this.unitPipe.transform('10', 'Metric');
}
Best solutions I can suggest is using an Optional/Default parameter:
Optional parameter - Change args to args?
OR
Default parameter - Change args to args = null // or some other default value
This will allow you to call the pipe function with 2 params, so no need for code changing in your service.
You can see in this TypeScirpt-Functions link, section called Optional and Default Parameters for more details.
Created a simple StackBlitz DEMO with your code to this in action. Initially you will see the error in SomeService file. Just change the pipe args param accordingly. refresh the page. The error is gone in SomeService.

How to get data from rest service when using angular2 server rendering

I have a simple component that retrieves data from the server.
#Component({
selector: 'app',
template: `
<h1>HEllo {{category}}</h1>
`,
providers: [
HTTP_PROVIDERS,
CategoryService
]
})
export class App implements OnInit {
private category;
constructor(private _categoryService: CategoryService){}
ngOnInit() {
this._categoryService.getCategories().subscribe(categories => this.category = categories.name);
}
}
And my service looks like
#Injectable()
export default class CategoryService {
constructor(private http: Http){}
private _categoriesUrl = 'http://localhost:3000/api/photo';
getCategories() {
return this.http.get(this._categoriesUrl)
.map(res => return res.json().data;)
.do(data => console.log(data))
.catch(this.handleError);
}
private handleError(error: Response) {
return Observable.throw(error.json().error || 'Server error');
}
}
It works well when I'm rendering it on client side but when I'm using angular2-universal-preview for rendering it on server side it throws an error
XMLHttpRequest is not defined
How could I make it working with server side rendering?
You need to add
providers: [
NODE_HTTP_PROVIDERS,
CategoryService
]
See also https://github.com/angular/universal/issues/292
I think it would be better to add NODE_HTTP_PROVIDERS to bootstrap(AppComponent, [NODE_HTTP_PROVIDERS]) instead of each (or some components) except when you have a good reason to request different instances of Http for each component.