Thursday 1 March 2018

Login authentication flow using angular auth guard - Angular 5

Angular 5Auth GuardLogin flow

In this article we will be discussing about implementing a simple login flow using angular auth guard. This article will cover below use cases for login flow.

• Login if user entered credentials are correct.
• After login navigate to dashboard.
• While click on back arrow (before navigating to login page), show alert message and log out.
• Logout option for logging out from application.
• After logout do not allow user to come back to the page on click of back/forward browser arrow.
• After logout do not allow user to access any pages except login page.

Please note, instead of calling an API I handled those in service file itself. I have used localStorage to set the user credential. Before looking into detailed code, please check below folder structure I used.

Login page


Let us start by creating a simple login page first.


<div class="container login">
    <section>
        <form (ngSubmit)="login()" #loginForm="ngForm">
            <div class="form-group">
                <label for="exampleInputEmail1">Email address</label>
                <input type="email" [(ngModel)]="model.emailId" class="form-control" id="exampleInputEmail1" name="emailId" aria-describedby="emailHelp"
                    placeholder="Enter email" required>
            </div>
            <div class="form-group">
                <label for="exampleInputPassword1">Password</label>
                <input type="password" [(ngModel)]="model.password" name="password" class="form-control" id="exampleInputPassword1" placeholder="Password"
                    required>
            </div>

            <button type="submit" class="btn btn-primary" [disabled]="!loginForm.form.valid">Login</button>
        </form>
        <br>
        <div class="alert alert-danger" *ngIf="showLoginWarningMessage" role="alert">
            Invalid email id or password
        </div>
    </section>
</div>


We have created a simple login form which has an email and password option for user to fill. Added a disabled attribute to disable the button until all the mandatory fields are filled. Now let us check the ts file.

loginComponent


import { Component, OnInit } from '@angular/core';
import { loginModel } from '../loginComponent/loginModel';
import { AuthServices } from '../services/authServices';
import { Router, ActivatedRoute } from '@angular/router';

@Component({
    selector: 'login-component',
    templateUrl: '../loginComponent/login.html'
})

export class loginComponent {
    public model: loginModel;
    showLoginWarningMessage: boolean = false;
    returnUrl: string = '';

    constructor(
        private auth: AuthServices,
        private router: Router,
        private route: ActivatedRoute) {
        this.model = new loginModel();
    }
    ngOnInit() {
        if (this.auth.validateUserTocken()) {
            //update your logic accordingly
            //this will trigger while user clicks on back button, 
            //before naviagting to login page
            alert('You will be logged out');
        }
        this.auth.logout();
        this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || 'dashboard';
    }

    login() {
        this.showLoginWarningMessage = false;
        if (this.auth.validateUserDetails(this.model)) {
            this.auth.setTocken();
            this.router.navigate([this.returnUrl]);
        } else {
            console.log('Invalid credentials');
            this.showLoginWarningMessage = true;
        }

    }
}

loginModel


export class loginModel{
    emailId : string;
    password: string;
}

In the login model we have declared required models. We have imported OnInit,Router,ActivatedRoute and AuthServices. AuthServices is our service file where we will be handling our authentication logics. In the login method we should invoke login api and depending on the result we need to set the token. Instead I have added some sample json in AuthServices. We will be using this token for validating the user session. If it is a valid user we are redirecting to dashboard. ngOnInit will trigger whenever the component has reloaded or initialized, so here we will be checking the ticket. For example if user is pressing back button we can show an alert message and trigger logout (just before user navigating to login page). You can enhance this logic for your needs as well.



AuthServices


Now let us check authService code.


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

@Injectable()

export class AuthServices {
    userData: Array<{}>;
    tocken: Number = 0;
    tockenDetails: String;
    constructor() {
        this.userData = [{
            emailId: 'prashobhps@XXXX.com', password: 'prashobh'
        }]
    }
    setTocken() {
        this.tocken = new Date().getTime();
        localStorage.setItem('tocken', JSON.stringify(this.tocken));
    }
    validateUserTocken() {
        this.tockenDetails = localStorage.getItem('tocken');
        //let _tocken = JSON.parse
        if (this.tockenDetails != null) {
            return true;
        } else {
            return false;
        }

    }
    //it should be from backend
    validateUserDetails(user) {
        let _userList = this.userData;
        for (var i = 0; i < _userList.length; i++) {
            if (user.emailId.toLowerCase() == _userList[i]['emailId'].toLowerCase() && user.password == _userList[i]['password']) {
                return true;
            }
        }
    }
    logout() {
        localStorage.clear();
    }
}

setTocken method will set a token, in our case I am just setting the current time. But in real scenario you can set the token which you are getting from the backend API and the same you can pass for all http request calls as well. We can pass token to all http calls using angular interceptor concept.

Read more: - Set token to all http request calls

AuthGuardService


Now let us check our AuthGuardService. Angular auth guard provides many options like below.

• CanActivate
• CanActivateChild
• CanDeactivate
• CanLoad
• Resolve

Here I have used CanActivate, check the code below.


import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'
import { AuthServices } from './authServices';

@Injectable()
export class AuthGuardService {
    constructor(private auth: AuthServices, private router: Router) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        if (this.auth.validateUserTocken()) {
            return true;
        } else {
            this.router.navigate(['/login'], {
                queryParams: {
                    returnUrl: state.url
                }
            });
            return false;
        }
    }
}

Here I have imported CanActivate,Router,ActivatedRouteSnapshot and RouterStateSnapshot. Inside the canActivate method validate the token, if it is valid return true else navigate to login page.

DashboardComponent


Check our simple DashboardComponent component as well before checking our router module.


<div class="container">

    <div class="header">
        <span class="float-left">
            <strong>Dashborad</strong>
        </span>
        <span class="float-right cursorPointer" (click)="logOut()">
            <strong>
                <u>Log out</u>
            </strong>
        </span>
    </div>
</div>


import { Component } from '@angular/core'
import { AuthServices } from '../services/authServices';
import { Router } from '@angular/router';

@Component({
    selector: 'dashboard-component',
    templateUrl: '../dashboard/dashboard.html'
})

export class DashboardComponent {
    constructor(private auth: AuthServices, private route: Router) { }
    logOut() {
        this.auth.logout();
        this.route.navigate(['login']);
    }
}

Router



import { Routes, RouterModule } from '@angular/router';
import { loginComponent } from './loginComponent/loginComponent';
import { DashboardComponent } from './dashboard/dashboardComponent';
import { AuthGuardService } from './services/authGuardService';

export const appRoutes: Routes = [
  { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuardService] },
  { path: 'login', component: loginComponent },
  { path: '**', redirectTo: '' }
]

Please note here we have added canActivate: [AuthGuardService] for our dashboard component. Now check module code.

Module



import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { appRoutes } from './app-routing.module';
import { FormsModule } from '@angular/forms';

// Components
import { AppComponent } from './app.component';
import { DashboardComponent } from './dashboard/dashboardComponent';
import { loginComponent } from './loginComponent/loginComponent';

// Services
import { AuthServices } from './services/authServices';
import { AuthGuardService } from './services/authGuardService';

@NgModule({
  declarations: [
    AppComponent,
    DashboardComponent,
    loginComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot(appRoutes)
  ],
  providers: [AuthServices, AuthGuardService],
  bootstrap: [AppComponent]
})
export class AppModule { }

We are done. Access the application it will navigate to login page. After login click on logout and try all the use cases.

In this article we have discussed about implementing a login authentication using Angular auth guard.

Angular 5Auth GuardLogin flow

Related Info

1. Http calls in Angular with simple examples.

2. 5 different ways for conditionally adding class - Angular.

3. Angular 2/4 routing with simple examples.

4. Reusable flashy message using Angular 4

1 comment :