Issue
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 {}
Solution
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:
- Provide the Angular Interceptor
app.module.ts:
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: HttpJwtAuthInterceptor,
multi: true,
},
{ provide: BASE_PATH, useValue: environment.apiUrl },
],
- 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;
}
- 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);
}
Answered By - Juan Vicente Berzosa Tejero
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.