A complete guide of authentication in angular 11

Angular is the most trending js frameworks for the single page App and front end development, now I am here to explain how the login/authentication functionality works in angular. Here are some basic concepts of authentication:

How the authentication works

In angular if a user enters the email and password on the login page then the email/password should be validated from the backend server, so we should call a login API and the API will validate the passed email/password at the server and return the response. so for calling the server we use the HTTP client module

a. How to call the backend API

Here in the code, we created a login function and we are passing login form data(email and password). this function sends a request to the server and receives the response from the server.

  // Sample code to call the server API
  login(formData:any):Observable<HttpResponse<any>>{
  
    return this.http.post<any>("Server API URL", formData,  { observe: 'response' })
    .pipe(
      tap((resp: HttpResponse<CommonResponse>) => {
        if(resp.headers.get('x-auth')){
        // Here we are checking the x-auth in header, if x-auth is present it means user is logged in
          this.cookieService.set("currentUser",resp.headers.get('x-auth'));
          // Storing the token in cookie
          this.loginStatus.next(true);
        }
        return resp;  
      }),
      catchError(this.handleError)
    );
  }
  

b. How to manage the authentication

To manage the authentication, angular provides the route guards , by using the guard we can restrict the end-user to open the page which one we don't want to open the page without login.

After getting the response from the server, we store the token in the cookie


  if(resp.headers.get('x-auth')){
    this.cookieService.set("currentUser",resp.headers.get('x-auth'));
    this.loginStatus.next(true);
  }
  

c. How auth guard works

Angular provides route guards to prevent users from navigating to parts of an app without authorization. The following route guards are available in Angular:

  • CanActivate
  • CanActivateChild
  • CanDeactivate
  • Resolve
  • CanLoad

Here in the below code, we are checking the cookie is there or not, if there is a cookie that we have put in the login function then the user is logged in.


  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    
    if (this.cookieService.get('currentUser')) {
        // logged in so return true
        return true;
    }

    // not logged in so redirect to login page with the return url
    this.router.navigate(['/login']);
    return false;
  }
  

This is the basic concept of authentication in angular, now times to build a login app in angular.

Installation Guide of angular App:

You can use angular CLI to create the project, install the Angular CLI, open the terminal and run the following command

  • npm install -g @angular/cli
  • Run ng new login-in-angular command to create the angular project, whiling running the command , the terminal will ask for some features details, by pressing enter you can choose the default settings.

You will see below the settings

ng new login-in-angular
? Do you want to enforce stricter type checking and stricter bundle budgets in the workspace?
  This setting helps improve maintainability and catch bugs ahead of time.
  For more information, see https://angular.io/strict No

? Would you like to add Angular routing? No

? Which stylesheet format would you like to use? CSS
CREATE login-in-angular/angular.json (3639 bytes)
CREATE login-in-angular/package.json (1209 bytes)
CREATE login-in-angular/README.md (1026 bytes)
CREATE login-in-angular/tsconfig.json (538 bytes)
CREATE login-in-angular/tslint.json (3185 bytes)
CREATE login-in-angular/.editorconfig (274 bytes)
CREATE login-in-angular/.gitignore (631 bytes)
CREATE login-in-angular/.browserslistrc (703 bytes)
CREATE login-in-angular/karma.conf.js (1436 bytes)
CREATE login-in-angular/tsconfig.app.json (287 bytes)
CREATE login-in-angular/tsconfig.spec.json (333 bytes)
CREATE login-in-angular/src/favicon.ico (948 bytes)
CREATE login-in-angular/src/index.html (303 bytes)
CREATE login-in-angular/src/main.ts (372 bytes)
CREATE login-in-angular/src/polyfills.ts (2830 bytes)
CREATE login-in-angular/src/styles.css (80 bytes)
CREATE login-in-angular/src/test.ts (753 bytes)
CREATE login-in-angular/src/assets/.gitkeep (0 bytes)
CREATE login-in-angular/src/environments/environment.prod.ts (51 bytes)
CREATE login-in-angular/src/environments/environment.ts (662 bytes)
CREATE login-in-angular/src/app/app.module.ts (314 bytes)
CREATE login-in-angular/src/app/app.component.html (25725 bytes)
CREATE login-in-angular/src/app/app.component.spec.ts (979 bytes)
CREATE login-in-angular/src/app/app.component.ts (223 bytes)
CREATE login-in-angular/src/app/app.component.css (0 bytes)
CREATE login-in-angular/e2e/protractor.conf.js (904 bytes)
CREATE login-in-angular/e2e/tsconfig.json (274 bytes)
CREATE login-in-angular/e2e/src/app.e2e-spec.ts (670 bytes)
CREATE login-in-angular/e2e/src/app.po.ts (274 bytes)
√ Packages installed successfully. 
  • cd login-in-angular
  • Run npm start or ng serve command to run the angular App

Now you will see the below screen in your browser

angular-app-works


Installation of required modules

  • npm i ngx-cookie-service

1. Create the login component

We will generate the login component so run ng g c login, this command will generate the login components and its file. your login component file structure will look like this

angular-login-component

  • You can use the below code for your login component

    
    //File location in login folder and file name login.component.ts
      import { Component, OnInit } from '@angular/core';
      import {FormGroup, FormControl, Validators} from '@angular/forms';
      import { Router } from '@angular/router';
      import { CommonResponse } from "../common-response";
      import { ApiService } from "../auth/api.service";
      @Component({
        selector: 'app-login',
        templateUrl: './login.component.html',
        styleUrls: ['./login.component.css']
      })
      export class LoginComponent implements OnInit {
    
        loginForm = new FormGroup({
          email: new FormControl('', [Validators.required, Validators.email]),
          password: new FormControl('', [Validators.required])
        })
        
        public loginError:String;
        constructor(private apiService:ApiService,private router: Router) { }
    
        ngOnInit() {}
    
        onSubmit(){  
          if(this.loginForm.valid){
            this.apiService.login(this.loginForm.value)
            .subscribe((data) => {
            console.log(data);
              if(data.status === 200 && !data.body.ErrorCode){
                this.router.navigate(['/dashboard']);
              }else{
                this.loginError = data.body.message;
              }        
            },
            error => this.loginError = error
            )
          }    
        }
      }
    
    
  • You can use the below code in your login.component.html file

    <!-- File location in login folder and file name login.component.html -->
    <div class="form-signin">
      <form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
          <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
          <p *ngIf="loginError">{{loginError}}</p>
          <mat-form-field>
              <input matInput placeholder="Enter your email" formControlName="email" required>
              <mat-error *ngIf="!loginForm.controls.email.valid">Please enter valid email id</mat-error>
          </mat-form-field>
          <mat-form-field>
              <input matInput placeholder="Enter your password" type="password" formControlName="password" required>
              <mat-error *ngIf="!loginForm.controls.password.valid">Please enter password</mat-error>
          </mat-form-field><br />
          <button type="submit" mat-raised-button color="warn">Login</button>
      </form>
    </div>
        
    

    2. Create the API service

    We are creating the API service to manage all backend API calls, you can create all your API here. Run ng g service api to generate the API service file.
    You can copy the below code in your API service file

    
        
        import { Injectable } from '@angular/core';
        import { HttpClient, HttpResponse, HttpErrorResponse } from '@angular/common/http';
        import { CookieService } from 'ngx-cookie-service';
        import { Observable, throwError, BehaviorSubject } from 'rxjs';
        import { catchError, map, tap } from 'rxjs/operators';
        import { Router } from '@angular/router';
    
        import { CommonResponse } from "./common-response";
        @Injectable({
          providedIn: 'root'
        })
        export class ApiService {
          private endPoint: string = "/api/";
          loginStatus = new BehaviorSubject<boolean>(this.hasToken());
          constructor(private http: HttpClient, private cookieService: CookieService, private router: Router) { }
          /**
           * 
           * @param formData as the login form data
           */
          login(formData: any): Observable<HttpResponse<CommonResponse>> {
            return this.http.post<CommonResponse>(this.endPoint + "login", formData, { observe: 'response' })
              .pipe(
                tap((resp: HttpResponse<CommonResponse>) => {
                  if (resp.headers.get('x-auth')) {
                    this.cookieService.set("currentUser", resp.headers.get('x-auth'));
                    this.loginStatus.next(true);
                  }
                  return resp;
                }),
                catchError(this.handleError)
              );
          }
          /**
           * 
           * @param error error 
           */
          private handleError(error: HttpErrorResponse) {
            if (error.error instanceof ErrorEvent) {
              // A client-side or network error occurred. Handle it accordingly.
              console.error('An error occurred:', error.error.message);
            } else {
              // The backend returned an unsuccessful response code.
              // The response body may contain clues as to what went wrong,
              console.error(
                `Backend returned code ${error.status}, ` +
                `body was: ${error.error}`);
            }
            // return an observable with a user-facing error message
            return throwError(
              'Something bad happened; please try again later.');
          };
    
          logout() {
            this.loginStatus.next(false);
    
            this.cookieService.deleteAll();
            this.router.navigate(['/login']);
          }
    
          /**
          *
          * @returns {Observable<T>}
          */
          isLoggedIn(): Observable<boolean> {
            return this.loginStatus.asObservable();
          }
          /**
           * if we have token the user is loggedIn
           * @returns {boolean}
           */
          private hasToken(): boolean {
            return this.cookieService.check('currentUser');
          }
        }
        
        
    

3. Create the response interface

Now we need to create a response interface that will be mapped with your returned data from the Server. in the ApiService file we used import { CommonResponse } from "./common-response"; create common-response.ts file

    export interface CommonResponse {
        ErrorCode: number,
        message:String,
        data:Object      
    }

4. Create the auth guard

As we already know guard is used to preventing users from navigating to parts of an app without authorization. Please run ng g guard auth to generate to auth guard. you will see the newly created file auth.guard.ts The command will ask for some settings, please enter for default settings.

    ng g guard auth
    ? Which interfaces would you like to implement? (Press <space> to select, <a> to
    ? Which interfaces would you like to implement? CanActivate
    CREATE src/app/auth.guard.spec.ts (331 bytes)
    CREATE src/app/auth.guard.ts (457 bytes)

You can use the below code in your auth guard file.


import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { CookieService } from 'ngx-cookie-service';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {
  constructor(private cookieService: CookieService, private router: Router) { }

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    
    if (this.cookieService.get('currentUser')) {
        // logged in so return true
        return true;
    }

    // not logged in so redirect to login page with the return url
    this.router.navigate(['/login']);
    return false;
  }
}


5. Create profile component

Now times to create a profile component that will only accessible if the user is logged in.

Run npm g c profile to generate the profile component

  • Plese use below code for profile.component.ts
import { Component, OnInit } from '@angular/core';
import { ApiService } from "../api.service";

@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
  styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
 

  constructor(private apiService:ApiService) { }

  ngOnInit() {
  }
  logout(){
    this.apiService.logout();
  }

}
  • Please use below code in profile.component.html

<p>Welcome to Dashboard</p>
<button mat-raised-button color="warn" (click)="logout()">Logout</button>

6. Routing & service implementation

We have created 2 components, ApiService & guard files not time to implement it in the AppModule.ts file

create app-routing.module.ts file to manage all pages. Please use the below code in your app-routing.module.ts file


import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from './login/login.component'; 
import { ProfileComponent } from './profile/profile.component';
import { AuthGuard } from "./auth.guard";

const routes: Routes = [
  { path: '', component: LoginComponent },
  { path: 'login', component: LoginComponent },
  { path: 'dashboard', component: ProfileComponent,canActivate: [AuthGuard]}
];

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

export class AppRoutingModule { }


Import the AppModule file in your Your app.module.ts file will look like this

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core'; 
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS  } from "@angular/common/http";
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { environment } from '../environments/environment';
import { LoginComponent } from './login/login.component';
import { ProfileComponent } from './profile/profile.component';
import { ApiService } from './api.service';
import { CookieService } from 'ngx-cookie-service'; 

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    ProfileComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule,
    ReactiveFormsModule,
    AppRoutingModule,
    BrowserAnimationsModule,
  ],
  providers: [ApiService, CookieService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Add router-outlet in your app.component.ts file

<router-outlet></router-outlet>

Conclustion

So we have implemented the code in the angular app, and I hope you are able to run it.

  • Run npm start to check run the app-login

  • http://localhost4200 Hit the URL in the browser

  • If you want to accessible http://localhost4200/dashboard, you will be redirected to http://localhost4200/login page, you can't access it without authorization.

  • Run npm start to run the server and test it.

Demo

After implementing the code now time to check the demo, so you can check the angular-login-demo

Please give it a try and I will be happy to answer your queries on my Twitter handle Twitter


Related blog of angular