In this article I explain how to protect an Angular application with JWT.
Of course, this Angular application depends on a backend to generate and validate the JWT. The backend part is done with Spring Boot and available in the following article.
I will separate this project in 3 components:
- A public content component;
- A private content component;
- And a login form component.
Finally, I will create an HTTP client service to wrap the requests to the backend and add the JWT to all the requests.
If you want a more detailed explanation, watch this video.
All the code of the article is available in the following repository.
Context
The JWT is a Json Web Token. From the frontend point of view, it’s a random string.
It can contain a lot of information, as the username, the validity date, the roles, the name of the user and more.
But in the frontend, as I don’t have the key to decrypt it, I will manage it as a random string.
The project will have a public content component which will be displayed by default. The public content component will request the backend to a public endpoint which requires no authentication.
In the homepage, I will also display a button to switch to the login form component. The login form component contains a login form. The user needs to fill it with the username and password. Once submit, the information will be sent to the backend.
The backend will respond with a token, with the JWT. I will save this JWT in the local storage. This way, even after reloading the page, I can access the JWT.
Once authenticated, I will display the private content component. This component also requests the backend, but this time is a protected endpoint. To request the protected endpoint, I send the JWT in the Authorization HTTP header.
I will also create an HTTP service to wrap the HTTP Client. This way, I can easily append the JWT to the Authorization HTTP header when needed.
Let’s start implementing all this.
Public Content Component
The public content component has only one field to be displayed.
<div>
<h2>Public content</h2>
<p>{{content}}</p>
</div>
The field content contains the response of the public endpoint from the backend. So, when loading the component, I request the backend.
export class PublicContentComponent {
content: string = "";
constructor(private http: MyHttpService) {}
ngOnInit(): void {
this.http.get("/public/messages").subscribe((data: Message) => this.content = data.message);
}
}
As said, I use my customer HTTP service instead of the HTTP Client. But it returns the Observable of the HTTP Client.
Private Content Component
The private content component is very similar to the public one. I change the title to differentiate them. But it also displays a single field.
<div>
<h2>Private content</h2>
<p>{{content}}</p>
</div>
As with the public content component, I request the backend when loading this component. But this time, I request a protected endpoint of the backend.
export class PrivateContentComponent {
content: string = "";
constructor(private http: MyHttpService) {}
ngOnInit(): void {
this.http.getPrivate("/messages").subscribe((data: Message) => this.content = data.message);
}
}
To request a protected endpoint, I use another method, getPrivate, which adds to the HTTP request the JWT.
Login Form Component
To create the Login form component I use Angular Material.
It is just a login form with two fields: username and password.
<form #loginForm="ngForm" class="login-form" (ngSubmit)="onSubmit()">
<mat-form-field class="login-full-width">
<mat-label>Username</mat-label>
<input name="username" ngModel matInput>
</mat-form-field>
<mat-form-field class="login-full-width">
<mat-label>Password</mat-label>
<input name="password" type="password" ngModel matInput>
</mat-form-field>
<button type="submit" mat-raised-button color="primary">Login</button>
</form>
The Typescript part of the login form has some more logic.
When submitting the form, I call the backend with the values of the login form.
I’ve also created an EventEmitter. This way, after the login is successful, I notify the root component to display the private content component.
export class LoginFormComponent {
@ViewChild('loginForm') loginForm!: NgForm;
@Output() onLoginEvent = new EventEmitter();
constructor(private http: MyHttpService) {}
onSubmit(): void {
this.http.login(
this.loginForm.value,
).subscribe((data: any) =>
{
this.http.setAuthToken(data.token);
this.onLoginEvent.emit());
}
}
}
Http Client Service
The HTTP Client service is the responsible to request the backend. It uses the Angular module of HTTP Client.
Inside, I will request the public endpoint of the backend, the protected endpoint and the login endpoint.
I will also have two methods to store and read the JWT from the local storage.
@Injectable({
providedIn: 'root'
})
export class MyHttpService {
constructor(private http: HttpClient) { }
get(url: string): any {
return this.http.get("http://localhost:8080" + url);
}
getPrivate(url: string): any {
return this.http.get("http://localhost:8080" + url, {headers: new HttpHeaders({"Authorization": "Bearer " + this.getAuthToken()})});
}
getAuthToken(): string | null {
return window.localStorage.getItem("auth_token");
}
setAuthToken(token: string | null): void {
if (token !== null) {
window.localStorage.setItem("auth_token", token);
} else {
window.localStorage.removeItem("auth_token");
}
}
login(credentials: any): any {
return this.http.get("http://localhost:8080/login");
}
}
The Root Component
Finally the root component. This is the responsible to display the adequate component depending on the user action.
By default, it will display the public content component.
If the user clicks on the login button, the root component will display the login form component.
And once authenticated, the root component displays the private content component.
<body>
<h1>My Awesome app</h1>
<button (click)="this.componentToShow = 'login'">Login</button>
<app-login-form *ngIf="componentToShow == 'login'" (onLoginEvent)="this.componentToShow = 'private'"/>
<app-public-content *ngIf="componentToShow == 'public'"/>
<app-private-content *ngIf="componentToShow == 'private'"/>
</body>
The TypeScript only has the definition of the variable to switch between all the components.
export class AppComponent {
componentToShow = "public";
}
Conclusion
Using the JWT to protect an application is a simple solution but very effective.
But the frontend must be careful when storing it.
I’ve chosen the local storage because it’s available only the current page. And remains after a refresh.



Leave a comment