JWT Authentication in Angular – Part 1 – Setup

Most web applications can be split into two parts – public and private. The public part of the app is open to everyone who knows the address. It can be an expanded portal or simply a login page. The private part includes functionality that requires special access. There are many authentication methods to verify user identity. One of them is JSON Web Tokens (JWT).

JWT – how does it work? 

JSON Web Tokens, in a nutshell, are string keys that help to confirm users’ identities. The claims in a JWT are encoded as a JSON object using Base64url. In the most common cases, the front-end app calls a server for authentication by sending information such as username and password. In response, it receives a session-specific token. It can be added to the authorization header of any non-public HTTP request. Based on that, a server can decide if the user performing an action is who he/she claims to be.

Angular

To set up user authentication in Angular, we need a server application with the JWT mechanism implemented. As a part of it, we need some endpoint that returns a token for an authenticated user. When it is ready, we can prepare the front-end configuration. 

The first step is to fetch a token. Then it can be saved in sessionStoragelocalStorage, or in the application state. Below you can see an example of how login and logout actions can be implemented as a part of an authentication service that additionally keeps and shares the token.  

@Injectable({ providedIn: 'root' })
export class AuthenticationService {
private currentUserSubject: BehaviorSubject<CurrentUser | null> =
new BehaviorSubject<CurrentUser | null>(
JSON.parse(localStorage.getItem('currentUser') as string)
);
public currentUser$: Observable<CurrentUser | null> =
this.currentUserSubject.asObservable();

constructor(private httpClient: HttpClient) {}

login(username: string, password: string): Observable<CurrentUser> {
return this.httpClient
.post<CurrentUser>(`${environment.apiUrl}/api/users/authenticate`, {
username,
password,
})
.pipe(
map((user) => {
localStorage.setItem('currentUser', JSON.stringify(user));
this.currentUserSubject.next(user);

return user;
})
);
}

logout(): void {
localStorage.removeItem('currentUser');
this.currentUserSubject.next(null);
}
}

Having saved the token, let’s create an interceptor. This class allows us to modify every HTTP request.

The token is added to every request sent to our API.

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor(private authenticationService: AuthenticationService) {}

intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const isAuthApiUrl = request.url.startsWith(environment.apiUrl);

if (!isAuthApiUrl) {
next.handle(request);
}

return this.authenticationService.currentUser$.pipe(
take(1),
switchMap((currentUser) => {
const currentUserToken = currentUser?.token;

if (!currentUserToken) {
return next.handle(request);
}

return next.handle(
request.clone({
setHeaders: {
Authorization: `Bearer ${currentUserToken}`,
},
})
);
})
);
}
}

The last step makes it work to provide our class in the main module of an application.

    providers: [
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
...

All set! Now we should be able to invoke actions with restricted access. In my next blog, I will share about handling expired or not valid tokens.

Tags: , ,