I'm new to Angular and I'm working on a project which uses Angular 5 as frontend and Laravel5.5 as backend.
What I am trying to do: I'm doing token based authentication to check if the correct user is sending the request to backend and if the token is not authenticated it will throw an exception and the user will be redirected to login page.
What I did: I followed a tutorial on Youtube which uses Authguard to verify the user, Below is my Code.
auth.guard.ts
import {Injectable} from "@angular/core";
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from "@angular/router";
import {UserService} from "../_services/user.service";
import {Observable} from "rxjs/Rx";
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private _router: Router, private _userService: UserService) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
console.log("AuthGuard.ts: "+ currentUser);
if (currentUser == null) {
this._router.navigate(['/login'], {queryParams: {returnUrl: state.url}});
return false;
}
else{
let response = this._userService.verify().map(
data => {
console.log("This is the data returned in authguard: "+data)
if (data !== null) {
// logged in so return true
// console.log("Not null");
return true;
}
// error when verify so redirect to login page with the return url
this._router.navigate(['/login'], {queryParams: {returnUrl: state.url}});
return false;
},
error => {
console.log('myerror');
// error when verify so redirect to login page with the return url
this._router.navigate(['/login'], {queryParams: {returnUrl: state.url}});
return false;
});
console.log("response : "+ typeof response);
return response;
}
}
}
In tutorial they used subscribe instead of map, but I did some research and it comes to me that we can not use subscribe with Observable.
My code though shows the error with subscribe and redirects the user to login page but with subscribe it does not return true or false so user is not redirected to next pages even if the credentials are correct.
Here is the code for verify function.
user.service.ts
import {Injectable} from "@angular/core";
import { HttpClient , HttpHeaders , HttpErrorResponse } from '@angular/common/http';
import {User} from "../_models/index";
import { Observable } from 'rxjs';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
interface DataResponse { status: string; }
@Injectable()
export class UserService {
constructor(private http: HttpClient) {
}
verify(): Observable<any> {
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
let headers = new HttpHeaders({'Authorization': 'Bearer ' + currentUser.token});
let abc = this.http.get<DataResponse>('http://127.0.0.1:8000/api/auth/verify', {headers:headers}).map(response =>{
// console.log("hjsdgjd: "+response.status );
response
})
.catch(this.errorHandler);
console.log("abc: " + abc);
return abc;
}
errorHandler(error: HttpErrorResponse){
console.log('some thing');
return Observable.throw(error.message);
}
}
This is my console when I modify the token
I solved this problem by using an interceptor.
What it does: It takes the http request from frontend and appends the header(containing token) to request before sending it to backend. And then it gets the response from backend, checks the response for error and if there is an error then it redirects the user to login page.
Here is my Interceptor Code.
AuthorizationInterceptor.ts
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { Router } from '@angular/router';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/throw';
@Injectable()
export class AuthorizationInterceptor implements HttpInterceptor
{
constructor(private _router: Router) { }
private handleAuthError(err: HttpErrorResponse): Observable<any> {
//handle your auth error or rethrow
console.log('server error : ', err);
if (err.status === 401 || err.status === 403) {
//navigate /delete cookies or whatever
localStorage.removeItem('currentUser');
this._router.navigate(['/login']);
// if you've caught / handled the error, you don't want to rethrow it unless you also want downstream consumers to have to handle it as well.
// return Observable.of(err.message);
}
return Observable.throw(err);
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
{
// console.log('resquest passing from interceptor....', req, next);
var authReq;
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
// console.log("In the Interceptor: " + req);
if (currentUser) {
// Clone the request to add the new header.
req = req.clone({headers: req.headers.set('Authorization', 'Bearer ' + currentUser.token)});
// console.log("Authentication Request: " + authReq);
// shortcut for above...
// const authReq = req.clone({setHeaders: {Authorization: authHeader}});
// Pass on the cloned request instead of the original request.
}
return next.handle(req).catch(x => this.handleAuthError(x));
}
}
Note: Do not forget to import the interceptor in your module.ts file. See This guide for building an interceptor.