Creating Angular 14 user authentication service

Creating Angular 14 user authentication service

In this step, we'll create the user authentication service with Angular 14.

First, we need to create the User model inside a src/app/user.ts file as follows:

export class User {
    _id: String;
    name: String;
    email: String;
    password: String;
}

Next, head back to your command-line interface and run the following command:

$ ng generate service auth

Next, open the generated src/app/auth.service.ts file and update it as follows:

import { Injectable } from '@angular/core';

import { Router } from '@angular/router';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';

import { Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';

import { User } from './user';


@Injectable({
  providedIn: 'root'
})

export class AuthService {
  API_URL: string = 'http://localhost:4000';
  headers = new HttpHeaders().set('Content-Type', 'application/json');
  currentUser = {};

  constructor(private httpClient: HttpClient,public router: Router){}

  register(user: User): Observable<any> {

    return this.httpClient.post(`${this.API_URL}/users/register`, user).pipe(
        catchError(this.handleError)
    )
  }

  login(user: User) {
    return this.httpClient.post<any>(`${this.API_URL}/users/login`, user)
      .subscribe((res: any) => {
        localStorage.setItem('access_token', res.token)
        this.getUserProfile(res._id).subscribe((res) => {
          this.currentUser = res;
          this.router.navigate(['users/profile/' + res.msg._id]);
        })
      })
  }

  getAccessToken() {
    return localStorage.getItem('access_token');
  }

  get isLoggedIn(): boolean {
    let authToken = localStorage.getItem('access_token');
    return (authToken !== null) ? true : false;
  }

  logout() {
    if (localStorage.removeItem('access_token') == null) {
      this.router.navigate(['users/login']);
    }
  }

  getUserProfile(id): Observable<any> {
    return this.httpClient.get(`${this.API_URL}/users/profile/${id}`, { headers: this.headers }).pipe(
      map((res: Response) => {
        return res || {}
      }),
      catchError(this.handleError)
    )
  }

  handleError(error: HttpErrorResponse) {
    let msg = '';
    if (error.error instanceof ErrorEvent) {
      // client-side error
      msg = error.error.message;
    } else {
      // server-side error
      msg = `Error Code: ${error.status}\nMessage: ${error.message}`;
    }
    return throwError(msg);
  }
}

We first import the necessary APIs like Router, HttpClient, HttpHeaders, HttpErrorResponse, Observable, throwError, catchError, map and the User class.

Next, we inject HttpClient via the service constructor and we define the API_URL, headers and currentUser variables. Next, we define the following methods:

  • The register() method which sends a POST request to the users/register endpoint for creating a user in MongoDB with information like name, email and password.
  • The login() method which sends a POST request to the users/loginendpoint and receives an HTTP responce with a JWT access token that will be used to allow the user to access the protected resources on the server.
  • The getAccessToken() method for accessing the token stored in the local storage after user login.
  • The isLoggedIn() method which returns true if the user is logged in or otherwise false.
  • The logout() method used to remove the access token from local storage and redirects the user to the login page.
  • The getUserProfile() method used to send a GET request to retrive the user profile,
  • The handleError() method used to handle any errors.

Step 7 - Attaching the JWT access token to requests using Angular 14 Http Interceptors

In this step, we'll create ann HTTP interceptor that will be used to attach the JWT access token to the authorization header of the ongoing requests.

Create the src/app/auth.interceptor.ts file and add the following code:

import { Injectable } from "@angular/core";
import { HttpInterceptor, HttpRequest, HttpHandler } from "@angular/common/http";
import { AuthService } from "./auth.service";

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    constructor(private authService: AuthService) { }

    intercept(req: HttpRequest<any>, next: HttpHandler) {
        const accessToken = this.authService.getAccessToken();
        req = req.clone({
            setHeaders: {
                Authorization: `JWT $[accessToken}` 
            }
        });
        return next.handle(req);
    }
}

We first import the necessary APIs such as Injectable , HttpInterceptor, HttpRequest, HttpHandler and AuthService. Next, we define the interceptor class and we decorate it with @Injectable, we inject the auth service via the constructor and we add the intercept() method where we call the getAccessToken() method to retrive the JWT token from local stoage and add it to the Authorization header of the outgoing request.

Next, we need to provide this interceptor in our app module. Open the src/app/app.module.ts file, import the interceptor class and add it to the providers array as follows:

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';

@NgModule({
  declarations: [...],
  imports: [HttpClientModule],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    }
  ],
  bootstrap: [...]
})

export class AppModule { }

Step 8 - Guarding/protecting routes from non authorized access

In this step, we'll create and set an authentication guard that will be used to protect the users/profile/ route from non loggedin users.

Head back to your command-line interface and run the following command:

$ ng generate guard auth

Next, open the src/app/auth.guard.ts file and add the following code:

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

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {

  constructor(
    public authService: AuthService,
    public router: Router
  ) { }

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    if (this.authService.isLoggedIn() !== true) {
      window.alert("Access not allowed!");
      this.router.navigate(['users/login'])
    }
    return true;
  }
}

Open the src/app/app-routing.module.ts file and import the authentication guard and apply it to the route as follows:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { UserProfileComponent } from './user-profile/user-profile.component';

import { AuthGuard } from "./auth.guard";


const routes: Routes = [
  { path: '', redirectTo: '/login', pathMatch: 'full' },
  { path: 'login', component: LoginComponent },
  { path: 'register', component: RegisterComponent },
  { path: 'profile/:id', component: UserProfileComponent, canActivate: [AuthGuard] }
];

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

export class AppRoutingModule { }

See you in the next tutorial