Http interceptor on response not working in angular 5 - angular5

I want the user to be automatically logged out if any api returns a 401 error response.And to do that I am intercepting every request and as soon as the error code comes 401 I am clearing the jwt token in my local storage and auth guard prevents the user from jumping to that route.But after implementing the interceptor(examples are very less for this and no mention in the docs as well) I am unable to hit any HTTP request.Below is my code.Thanks in advance.
import { Injectable, Injector } from '#angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse} from '#angular/common/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/observable/throw'
import 'rxjs/add/operator/catch';
#Injectable()
export class ResponseInterceptor implements HttpInterceptor {
constructor() {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).do((event: HttpEvent<any>) => {
}, (err: any) => {
if (err instanceof HttpErrorResponse) {
// do error handling here
console.log('and the error is ');
console.log(err)
}
});
}
}

If it goes with error why you need to track every request if you could only catch needed?
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((err:string, caught:Observable<any>)=>this.handleHttpClientError(err, caught))
);
}
handleHttpClientError(error: any, caught: Observable<any>)
{
if(error.status == 401){
... your logic here ...
}
return new EmptyObservable<Response>();
// or return Observable.throw('Auth error');
}

Related

Angular catch errors in a central place or component

I have an Angular 9 project that works with a .NET Core WEB API.
The site features user authentication that is role based. The API returns two types of errors: 401 and 403 depending on the authorization rules for a controller method.
My goal is to display a message in my layout component when the API returns any of these errors, for example:
401: 'Session has expired, please log in.'
403: 'You do not have access to this feature.'
Is there any way to catch these errors in Angular and know which one was thrown?
You can use interceptors to catch every http requests. In your case you need to catch response. To do it;
At the module which you provided HttpClientModule
(app/core).module.ts
import { HTTP_INTERCEPTORS, HttpClientModule } from '#angular/common/http';
import { HttpErrorInterceptor } from '<path of interceptor>';
#NgModule({
imports: [HttpClientModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: HttpErrorInterceptor,
multi: true,
},
]
});
error.interceptor.ts
export class HttpErrorInterceptor implements HttpInterceptor {
constructor(
// your injections if you need
) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((err: HttpErrorResponse) => {
switch (err.status) {
case 401: {
this.handle401();
}
case 403: {
this.handle403();
}
}
return throwError(err);
}),
);
}

Customise the response on verification failure for a jwt Strategy NestJs

I successfully implemented a jwt strategy for authentication using nestJs.
Below is the code for the jwt strategy
import { ServerResponse } from './../helpers/serverResponse.helper';
import { Injectable, UnauthorizedException, HttpStatus } from '#nestjs/common';
import { PassportStrategy } from '#nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { config as env } from 'dotenv';
import { Bugsnag } from '../helpers/bugsnag.helper';
env();
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor(
private readonly logger: Bugsnag,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET_KEY,
passReqToCallback: true,
});
}
async validate(payload, done: Function) {
try {
const validClaims = await this.authService.verifyTokenClaims(payload);
if (!validClaims)
return done(new UnauthorizedException('invalid token claims'), false);
done(null, payload);
} catch (err) {
this.logger.notify(err);
return ServerResponse.throwError({
success: false,
status: HttpStatus.INTERNAL_SERVER_ERROR,
message: 'JwtStrategy class, validate function',
errors: [err],
});
}
}
}
I saw here that the validate function will be called only when a valid token was provided in the request headers and I'm okay with that. However, I would like to know if it is possible to customize the response object which is sent in that case (invalid token provided).
If yes, how do I do that ?
You can use a exception filter to catch UnauthorizedExceptions and modify the response there if you'd like. The other option would be extending the AuthGuard('jwt') mixin class and adding in some logic around a try/catch for the super.canActivate(context), then in the error read what the reason is and throw a specific UnauthorizedException with your custom message
You can use the AuthGuard('jwt')'s handleRequest method to throw any exception on JWT Validation failure.
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
handleRequest(err: any, user: any, info: any, context: any, status: any) {
if (info instanceof JsonWebTokenError) {
throw new UnauthorizedException('Invalid Token!');
}
return super.handleRequest(err, user, info, context, status);
}
}
JsonWebTokenError comes from jsonwebtoken library, which is used internally by passport.

Interceptor code not getting called when using http.post

I have written (copied from SO!) the following interceptor code. I want to modify the outgoing request and also intercept the response. However, I have noticed that the response never gets intercepted. Why? Is it because I am using Rxjs?
Interceptor code
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from "#angular/common/http";
import {Injectable} from "#angular/core";
import {Observable} from "rxjs/Observable";
import 'rxjs/add/operator/do';
#Injectable()
export class CustomInterceptor implements HttpInterceptor {
constructor() {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log("outgoing request",request);
request = request.clone({
withCredentials: true
});
console.log("new outgoing request",request);
return next
.handle(request)
.do((ev: HttpEvent<any>) => {
if (ev instanceof HttpResponse) {
console.log('processing response', ev); //I DON'T SEE THIS PRINT
}
});
}
}
I am sending the request to the server as follows
return this.http.post(/*this.API_URL+*/this.SIGNIN_USER_URL,body,httpOptions)
.map(response=>{
console.log('response from backend service',response); //I SEE THIS PRINT
let result= <ServerResponse>response;
console.log("result is "+result.result+' with additional information '+result.additionalInformation)
return result;
})
.catch(this.handleError);
Why is the interceptor code not getting hit?
Update - the provider code snippet in app.module.ts
providers: [WebToBackendInterfaceService, //some other provider
{
provide: HTTP_INTERCEPTORS,
useClass: CustomInterceptor , //interceptor provider
multi: true
}],
I added a print between do and if (console.log("got an event",ev). I can see the following message on console `got an event
{…}
​body: Object { result: "success", "additional-info": "found user" }
​headers: {…}
​lazyInit: function lazyInit()
​​lazyUpdate: null
​​normalizedNames: Map
​​​size: 0
​​​<entries>
​​​__proto__: Object { … }
​​__proto__: Object { has: has(), get: get(), keys: keys(), … }
​ok: true
​status: 200
​statusText: "OK"
​type: 4
​url: "http://localhost:9000/ws/users/signin"
​__proto__: Object { constructor: HttpResponse(), clone: clone() }`
I dont know why the if statement isn't getting executed as it seems the event is of type HttpResponse (referring to ​__proto__: Object { constructor: HttpResponse(), clone: clone() })

Angular2: canActivate

Currently I want to implement canActivate function, everything I want is to send a request to server each time page requested and get true/false in a json response in order to understand is user authenticated and permitted to review current page.
And it seems that I completely stuck with observable and promise objects, which is new for me, what is what I have so far.
import { Injectable } from '#angular/core';
import {CanActivate, Router} from '#angular/router';
import { Http, Response } from '#angular/http';
import {Observable, Observer, Subject} from "rxjs/Rx";
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router, private http: Http) {}
canActivate() {
if (this.isAuthenticated()) {
return true;
} {
this.router.navigate(['404']);
return false;
}
}
isAuthenticated() : Observable<boolean> {
var subject = new Subject<boolean>();
this.http.get("/index.php?module=Api&action=IsAuthenticated")
.map((res : Response) => res.json())
.subscribe(res => {
console.log("next: returning true");
subject.next(true);
}, (res) => {
console.log("next: returning false");
subject.next(false);
});
return subject.asObservable().first();
}
}
A few changes
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router, private http: Http) {}
canActivate() {
return this.isAuthenticated().first(); // not sure if `.first() is still necessary
}
isAuthenticated() : Observable<boolean> {
return this.http.get("/index.php?module=Api&action=IsAuthenticated")
.map((res : Response) => res.json())
.catch(err => return Observable.of(false))
.map(res => {
return true
});
}
}
If isAuthenticated() does some async execution, we don't get true or false back, we get an Observable that emits a true or false value eventually
What we do is to return the observable we get from isAuthenticated()
In isAuthenticated with return the observable we get fromthis.http.get()and transform the emitted event. It seems the response from the server (res.json()) is not used. Therefore we usecatch()to returnfalsein case of an error andtrue` otherwise.
Because the response from the server is not used .map((res : Response) => res.json()) could be omitted, except this is where you expect an error from that should case false to be returned.
Also your production code might look different and require the response to be processed.
We don't subscribe anywhere, because this is what the router is doing when an Observable is returned from canActivate and if we call subscribe() we get a Subscription instead of an Observable.
canActivate can return either Observable<boolean>, Promise<boolean> or boolean.
As you are depending on asynchronous checking you cannot return a boolean.
It however looks like you could just simply do
canActivate(): Observable<boolean> {
return this.isAuthenticated();
}
I'm no expert on Observable yet, but it would also be easy for you to chain on a call to redirect if you where not authorised.
Here is the solution that works for me:
canActivate() {
return this.http.get("/index.php?module=Api&action=IsAuthenticated")
.toPromise()
.then(this.extractData)
.catch(this.handleError);
}

Angular2 auth guard with http request and observables

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.