A complete guide of authentication in angular 11
Tags: angular
Published: 2021-04-21 14:59:20


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.

```javascript
  // 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](https://angular.io/guide/router) , 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.<br/>

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

  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.

```javascript

  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 

```javascript
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-login](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flfrj8apboh93jew75tl.png)

<br/>

### 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p2lqdep5jofbiiqf2f9l.png)

- You can use the below code for your login component
  ```javascript
  
  //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

  ```html
  <!-- 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.
 <br/>
 You can copy the below code in your API service file
  
  ```javascript

	
	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 

  ```javascript
	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.
  
  ```javascript
	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.
  
  ```javascript
  
  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
```javascript
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
```javascript

<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

```javascript

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

```javascript
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

```html
<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](https://admiring-ardinghelli-c7c662.netlify.app/) 


Please give it a try and I will be happy to answer your queries on my Twitter handle [Twitter](https://twitter.com/eavnitech)