0

Currently, after logging in I'm able to get the JWT to the frontend. My app currently has a logging page as the landing page and as soon as the user logins the route checks for authentication to redirect to the guarded home path.

My first intuition was to send a boolean from the backend (Django) and use that to create a guard. But I keep seeing that seems to be better practice to handle this in the front end.

What I did was create an auth.service.ts and an auth.guard.ts. In the service, I try to retrieve the token from the browser and then verify that it hasn't expired. Then I call that method on the guard and return a boolean. Problem is that every time I look for the token in the local storage, I get back null.

Is there any better way to get achieve this?

auth.guard.ts

import { Injectable } from '@angular/core';
import {
  ActivatedRouteSnapshot,
  CanActivate,
  Router,
  RouterStateSnapshot,
  UrlTree,
} from '@angular/router';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ):
    | Observable<boolean | UrlTree>
    | Promise<boolean | UrlTree>
    | boolean
    | UrlTree {
    console.log(this.authService.isAuthenticated());
    if(!this.authService.isAuthenticated()){
      this.router.navigate(['/login']);
      return false;
    }
    return true;
  }
}

auth.service.ts

import { Injectable } from '@angular/core';
import { JwtHelperService } from '@auth0/angular-jwt';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  public jwtHelper: JwtHelperService = new JwtHelperService();
  constructor() { }

  isAuthenticated(){
    const jwt = localStorage.getItem('token');
    return !this.jwtHelper.isTokenExpired(jwt!);
  }
}

app-routing.module.ts

...
import { AuthGuard } from './user/services/auth.guard'

const routes: Routes = [
  {
    path: '',
    component: LandingComponent,
    children: [
      { path: '', component: HomeComponent, canActivate: [AuthGuard],},
      { path: 'home', component: HomeComponent, canActivate: [AuthGuard],},
      {
        path: 'cohort-charts',
        component: CohortChartsComponent,
        children: [
          { path: 'selection', component: CohortSelectionComponent },
          { path: 'edit', component: CohortEditComponent },
          { path: '', redirectTo: 'selection', pathMatch: 'full' },
        ],
      },
    ],
  },
  {
    path: 'login',
    component: LoginComponent,
  },
  { path: '**', redirectTo: '' },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

1 Answers1

0

I guess that every time you look for the token in the local storage, you get back null because you aren't saving the token, or if you do, you are trying to store the token as object, not serialized (as string, stringifying it) , so it doesn't store, or when you get it, you aren't pasing it.

Any way, I guess that the best practice to manage the whole jwt/authentification section, would be with an interceptor:

And Interceptor is a service which intercepts all your http calls, and you cans set that it does something authomatically (for instance, magaging the jwt).

More info about how to Adding and updating headers and how to Use the interceptor for Intercepting requests and responses:

https://angular.io/guide/http#adding-and-updating-headers

https://angular.io/guide/http#intercepting-requests-and-responses

I leave you a glimpse of how you do it /what you need:

  1. Provide the Angular Interceptor
app.module.ts:
 providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: HttpJwtAuthInterceptor,
      multi: true,
    },
    { provide: BASE_PATH, useValue: environment.apiUrl },
  ],
  1. First, in your auth.service.ts, 2 methods to store/get the token
// STORE the token in localstore:
setToken(token:string){

   // First, serialize it (but just if token is not string type).
   const tokenString:string = JSON.stringify( token );
 
   localStorage.setItem('token', tokenString);
}
 
// READ the token from localstorage and Deserialize
getToken(): string | null{
 
   let token = localStorage.getItem( 'token' );
 
   if( token !=null){

       // You just need to parse if you serialized it inside setToken() method
       token = JSON.parse(carItemsString);
  }
 
  return token;
 
}
  1. Then, in your interceptor:
import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
} from '@angular/common/http';

import { AuthService } from '../_services/auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(private authService: AuthService) {}

  intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
  
  const url="\yourAPI\endpoint";
  
    //  Get your token
    cont myToken = this.authService.getToken();
     
    // Add authorization header with token if available   
 
    if (myToken) {
    
       request = request.clone({
          setHeaders: {
            Authorization: `Bearer ${myToken}`,
            'Content-Type': 'application/json',
          },
          url,
        });
        
    } 
    
    …
return next.handle(request);

    }
  • After stringifying it I still get null. Also in the app module, the provide with useValue, what do we provide in those? – Diego Burela Feb 02 '22 at 01:13
  • Everything has been working and clear until I create the interceptor. What should I replace " ${currentUser.user.api_token}" with? And wouldn't it be easier to just return true or false depending on whether there is a token or not for the interceptor? Thank you for the help @Juan Vicente Berzosa Tejero – Diego Burela Feb 05 '22 at 04:56
  • Sorry, it wat just "Bearer ${myToken}", I've just edited. Any way, **the main point to solve your " Problem is that every time I look for the token in the local storage, I get back null", is the step 2)** (How to store/get values in the LocalStorage, serializing your objects as tring, because LocalStorage only can manage strings). The interceptor is just a "good practice" (but you can do it in another way, inserting the header "by hand" in your calls to the back, for instance). – Juan Vicente Berzosa Tejero Feb 05 '22 at 07:23
  • I accepted the answer since it helped me with finding what is in the local storage. I'm still not sure what is the difference between creating a guard and an interceptor. How is setting an authorization header helping me with guarding certain routes? – Diego Burela Feb 07 '22 at 09:11