Share and Learn To Write Code!

Programming articles, which help you to build your application. If something you are stuck to code and complete the program, just find here your question-related articles.

Share with   

Sign in or login demo using jwt token from rest api in angular 8 | 9

Here we are implementing a login page for angular 8 or 9 using JWT token from rest API. Implementing auth interceptor for adding a token into the HTTP request.
angular   signin   login   javascript  

Introduction


Here we are implementing a login page for angular 8 or 9 using JWT token from rest API. Implementing auth interceptor for adding a token into the HTTP request for calling rest api with authorization token.


Prerequisite


1) Node js. Click here to install nodejs.

2) Angular Framework, If you do not know how to install angular in your system please check this article install Angular

3) Bootstrap, click here to install bootstrap.


Follow the below steps


1) Create Angular App

ng new login_system

This will create login_system angular project.


2) Open this app project in visual code or any other which is used by you.


3) Install all prerequisites packages for angular app

npm install jquery bootstrap ngx-webstorage --save

In this project we need bootstrap, jquery if needed and ngx-webstorage.


ngx-webstorage this library provides an easy to use service to manage the web storages (local and session) from your Angular application. It provides also two decorators to synchronize the component attributes and the web storages. We can also use simple localstorage instead of ngx-webstorage.


4) Go to src > angular.json file to register jquery and bootstrap in style and script see below code.

// angular.json

"styles": [
	 "src/styles.scss",
	 "node_modules/bootstrap/dist/css/bootstrap.css"
],

"scripts": [
	 "node_modules/jquery/dist/jquery.min.js",
]


5) Create components for login, home and profile page.

Here we have two ways to create components i.e. one is create single component and another is create multiple component at time using for loop in command prompt.

To create single component just use

ng generate component component-name

or

ng g c component-name


To create multiple component in one command use this

for %n in (component-name-1, component-name-2, ...) do ng g c %n

Here we use for do loop for multiple time recurring method.

(component-name-1, component-name-2 ) this is used to add your component names

%n is variable to pass into generate component for passing your component name.


For our app create components.

ng g c components/home
ng g c components/profile
ng g c components/login
ng g c components/navbar

or

for %n in (navbar,home,profile,login) do ng g c components/%n


6) Create Services for login and profile

ng g s shared/services/login
ng g s shared/services/profile
ng g s shared/services/my-shared
ng g s shared/services/auth-guard

or

for %n in (profile,login,my-shared,auth-guard) do ng g s shared/services/%n


7) Create token interceptor.

ng g interceptor shared/intercetor/token


This will create all components, services and classes and interceptor into src > app directory.


8) Register profile and login services and interceptor into providers inside app.module.ts

providers : [
    ProfileService, LoginService, MySharedService, AuthGuard,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: TokenInterceptor,
      multi: true
    },
],


9) Register http module, reactive form module and ngx web storage module into imports inside app.module.ts


imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    ReactiveFormsModule,
    NgxWebstorageModule.forRoot(),
],


Finally your app.module.ts is like bellow

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './components/home/home.component';
import { ProfileComponent } from './components/profile/profile.component';
import { LoginComponent } from './components/login/login.component';
import { ProfileService } from './shared/services/profile.service';
import { LoginService } from './shared/services/login.service';
import { NavbarComponent } from './components/navbar/navbar.component';
import { MySharedService } from './shared/services/my-shared.service';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { TokenInterceptor } from './shared/intercetor/token.interceptor';
import { ReactiveFormsModule } from '@angular/forms';
import { NgxWebstorageModule } from 'ngx-webstorage';
import { AuthGuard } from './shared/services/authgurd.service';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    ProfileComponent,
    LoginComponent,
    NavbarComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    ReactiveFormsModule,
    NgxWebstorageModule.forRoot(),
  ],
  providers: [
    ProfileService, LoginService, MySharedService, AuthGuard,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: TokenInterceptor,
      multi: true
    },
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}



10) Design simple navbar

// src > app > components > navbar > navbar.component.html

<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="#">Authentication System</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
    aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>


  <div class="collapse navbar-collapse" id="navbarSupportedContent">
    <ul class="navbar-nav ml-auto">
      <li class="nav-item" [routerLinkActive]="['active']">
        <a class="nav-link" routerLink="home">Home</a>
      </li>
      <li class="nav-item" [routerLinkActive]="['active']" *ngIf="sharedService.isAuth">
        <a class="nav-link" routerLink="profile">Profile</a>
      </li>
      <li class="nav-item" [routerLinkActive]="['active']" *ngIf="!sharedService.isAuth">
        <a class="nav-link" routerLink="login">Login</a>
      </li>
      <li class="nav-item" *ngIf="sharedService.isAuth">
        <a class="nav-link" href="#"  (click)="logout()">Logout</a>
      </li>
    </ul>
  </div>
</nav>



// src > app > components > navbar > navbar.component.ts

import { Component } from '@angular/core';
import { MySharedService } from 'src/app/shared/services/my-shared.service';
import { LoginService } from 'src/app/shared/services/login.service';

@Component({
  selector: 'app-navbar',
  templateUrl: './navbar.component.html',
  styleUrls: ['./navbar.component.css']
})
export class NavbarComponent {

  constructor(public sharedService : MySharedService, private authService : LoginService) {}
  logout(){
    this.authService.signOut();
  }
}


11) App component like this and use router outlet to get other home, profile and login pages and add navbar selector to display navbar to all the pages.

// src > app > app.component.html

<app-navbar></app-navbar>
<router-outlet></router-outlet>


12) Login Page Code

// src > app > components > login > login.component.html

<div class="jumbotron text-center">
    <h2>Login Component</h2>
</div>
<div class="container text-center">
    <div class="row justify-content-center">
        <div class="col-lg-4">
            <form [formGroup]="complexForm" (ngSubmit)="signIn()">
                <input type="text" id="userName" class="form-control" placeholder="Username"
                    [(ngModel)]="model.username" formControlName="username"
                    [ngClass]="{ 'is-invalid': !f.username.valid && f.username.errors && f.username.touched }">
                <p *ngIf="f.username.errors" class="invalid-feedback">
                    <span *ngIf="f.username.errors.required && f.username.touched">Username is
                        required.</span>
                </p>
                <input type="text" id="userPassword" class="form-control" placeholder="Password"
                    [(ngModel)]="model.password" formControlName="password"
                    [ngClass]="{ 'is-invalid': !f.password.valid && f.password.errors && f.password.touched }">
                <p *ngIf="f.password.errors" class="invalid-feedback">
                    <span *ngIf="f.password.errors.required && f.password.touched">Password is
                        required.</span>
                </p>
                <br>
                <div class="wrapper">
                    <span class="group-btn">
                        <button type="submit" class="btn btn-primary btn-block">login</button>
                    </span>
                </div>
            </form>
        </div>
    </div>
</div>


// src > app > components > login > login.component.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { LoginService } from 'src/app/shared/services/login.service';
import { Router } from '@angular/router';

class Auth {
    username : string = '';
    password : string = '';
}?

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})

export class LoginComponent implements OnInit {

  complexForm: FormGroup;
  model: Auth;

  constructor(private formBuilder: FormBuilder,private _authService : LoginService,private _route : Router) {
    this.model = new Auth();
    this.complexForm = this.formBuilder.group({
      username: ['', Validators.required],
      password: ['', Validators.required],
    });
  }

  get f() { return this.complexForm.controls; }

  ngOnInit(): void {
    this._authService.signOut();
  }

  signIn() {
    if (this.complexForm.invalid) {
      for (var i in this.complexForm.controls) {
        this.complexForm.controls[i].markAsTouched();
      }
      return;
    }
    let postData = {
      username: this.model.username,
      password: this.model.password,
    }
    this._authService.login(postData).subscribe(res=>{
      this._route.navigate(["/home"]);
    })
  }
}


// src > app > shared > services > login.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse} from '@angular/common/http';
import { MySharedService } from './my-shared.service';
import { environment } from 'src/environments/environment';
import { Observable, throwError } from 'rxjs';
import { LocalStorageService } from 'ngx-webstorage';
import { map, catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class LoginService {
  headers: HttpHeaders = new HttpHeaders();
  headerOptions: any;
  constructor(private http: HttpClient,private _sharedService: MySharedService,private _localStorageService: LocalStorageService) {
    this.headers.set('Content-Type', 'application/x-www-form-urlencoded');
    this.headerOptions = {
      headers: this.headers,
      observe: 'response',
      responseType: 'json'
    }
    this.sharedData();
  }

  sharedData() {
    let authData = this._localStorageService.retrieve('AuthrizationData');
    if (authData) {
      this._sharedService.fullname = authData.fullname;
      this._sharedService.token = authData.token;
      this._sharedService.isAuth = true;
      this._sharedService.token_type = authData.token_type;
    } else {
      this._sharedService.fullname = '';
      this._sharedService.token = '';
      this._sharedService.token_type = '';
      this._sharedService.isAuth = false;
    }
  }

  login(obj: Object): Observable<any> {
    const url = environment.baseURI + "login";
    const postData = obj;
    return this.http.post(url, postData, this.headerOptions).pipe(
      map((res: any) => {
        console.log(res);
        const response = res.body;
        this.signOut();
        this._localStorageService.store('AuthrizationData', { token: response.token, expire: response.expiry, token_type: response.token_type, fullname: response.name })
        this.sharedData();
        return true;
      }),
      catchError(e => this._error(e))
    );
  }

  _error(error: HttpErrorResponse): any {
    alert(error.error.error);
    return throwError(error);
  }

  signOut() {
    this._localStorageService.clear('AuthrizationData');
    this.sharedData();
  }
}?


13) Home Page Code


// src > app > components > home > home.component.html

<div class="jumbotron text-center">
    <h2>Home Component</h2>
    <br>
    <h5> Hello {{userName}} </h5>
</div>
<div class="container">
    <div class="text-center">
        <h5> This home page is seen without any authentication no need for login. </h5>
        <br>
        <h5 *ngIf="!isLoggedIn">
            You need to login your account to see profile page.
        </h5>
        <h5 *ngIf="isLoggedIn">
            You are now logged in you can see your profile page.
        </h5>
    </div>
</div>

// src > app > components > home > home.component.ts

import { Component, OnInit } from '@angular/core';
import { MySharedService } from 'src/app/shared/services/my-shared.service';
import { LoginService } from 'src/app/shared/services/login.service';

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

  userName: string = 'User';
  isLoggedIn: boolean = false;

  constructor(private sharedService: MySharedService, private authService: LoginService) { }

  ngOnInit(): void {
    this.isLoggedIn = this.sharedService.isAuth;
    if (this.isLoggedIn) {
      this.userName = this.sharedService.fullname;
    }
  }
}


14) Profile Page Code


// src > app > components > profile > profile.component.html

<div class="jumbotron text-center">
    <h2>Profile Component</h2>
</div>
<div class="container">
    <div class="text-center">
        <h5> This profile page is seen only if you have authentication. </h5>
        <br>
        <h5> Name : <span style="color: brown;">{{model.first_name}} {{model.last_name}}</span></h5>
        <h5> Email : <span style="color: brown;">{{model.email}}</span></h5>
        <h5> Username : <span style="color: brown;">{{model.username}}</span></h5>
    </div>
</div>

// src > app > components > profile > profile.component.ts

import { Component, OnInit } from '@angular/core';
import { ProfileService } from 'src/app/shared/services/profile.service';

class User {
  email: string = '';
  first_name: string = '';
  last_name: string = '';
  pk: number = 0;
  username: string = '';
}

@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
  styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
  model: User = new User();
  constructor(private _service: ProfileService) {}

  ngOnInit(): void {
    this.getUserData();
  }

  getUserData() {
    this._service.getUserInfo().subscribe(response => {
      this.model = response;
    })
  }
}


// src > app > shared > services > profile.service.ts

import { Injectable } from '@angular/core';
import { HttpHeaders, HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { environment } from 'src/environments/environment';
import { map, catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class ProfileService {
  headers: HttpHeaders = new HttpHeaders();
  headerOptions: any;
  constructor(private http: HttpClient) {
    this.headers.set('Content-Type', 'application/x-www-form-urlencoded');
    this.headerOptions = {
      headers: this.headers,
      observe: 'response',
      responseType: 'json'
    }
  }

  getUserInfo(): Observable<any> {
    const url = environment.baseURI + "userinfo";
    return this.http.get(url, this.headerOptions).pipe(
      map((res: any) => {
        const response = res.body;
        return response;
      }),
      catchError(e => this._error(e))
    );
  }

  _error(error: HttpErrorResponse): any {
    alert(error.error.error);
    return throwError(error);
  }
}


15) Make service file for storing all the data like get and set informational data for shared into every pages.

// src > app > shared > my-shared.services.ts

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

@Injectable({
  providedIn: 'root'
})
export class MySharedService {

  static token: string = '';
  get token(): string { return MySharedService.token; }
  set token(val: string) { MySharedService.token = val; }

  static fullname: string = '';
  get fullname(): string { return MySharedService.fullname; }
  set fullname(val: string) { MySharedService.fullname = val; }

  static token_type: string = '';
  get token_type(): string { return MySharedService.token_type; }
  set token_type(val: string) { MySharedService.token_type = val; }

  static isAuth: boolean = false;
  get isAuth(): boolean { return MySharedService.isAuth; }
  set isAuth(val: boolean) { MySharedService.isAuth = val; }
}


16) Add Interceptor into app for passing token authorization into every http api call

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
import { isUndefined, isNullOrUndefined } from 'util';
import { MySharedService } from '../services/my-shared.service';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  
constructor(private _sharedService: MySharedService) { }
  intercept(req: HttpRequest<any>, next: HttpHandler):
    Observable<HttpEvent<any>> {
    if (!isUndefined(this._sharedService.isAuth)) {
      var isAuth = this._sharedService.isAuth as boolean;
      if (isAuth == true) {
        const authHeader = this._sharedService.token_type + ' ' + this._sharedService.token;
        if (!isNullOrUndefined(authHeader)) {
          const authReq = req.clone({ headers: req.headers.set('Authorization', authHeader) });
          return next.handle(authReq);
        }
      }
      else {
        const authReq = req.clone({ headers: req.headers.set('Access-Control-Allow-Origin', '*') });
        return next.handle(req);
      }
    }
  }
}


17) Use route guard for authentication pages. If user open that page forcefully and you want to show page only after login then need to add route guard.

// src > app > shared > services > authguard.service.ts

import { Injectable } from '@angular/core';
import { Router, CanActivate } from '@angular/router';
import { MySharedService } from './my-shared.service';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(public sharedservice: MySharedService, public router: Router) {}
  canActivate(): boolean {
    if (!this.sharedservice.isAuth) {
      this.router.navigate(['login']);
      return false;
    }
    return true;
  }
}


18) Finally set all app routing with auth guard

// src > app > app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './components/home/home.component';
import { ProfileComponent } from './components/profile/profile.component';
import { LoginComponent } from './components/login/login.component';
import { AuthGuard } from './shared/services/authgurd.service';

const routes: Routes = [
  { path: "", redirectTo: "home", pathMatch: "full" },
  { path: "home", component: HomeComponent },
  { path: "profile", component: ProfileComponent, canActivate: [AuthGuard] },
  { path: "login", component: LoginComponent }
];

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


Finally done.


Output of the above app


Home page


Login Page

0replies
Leave a reply
46
Registerd Users
87
Total Subscribers
59
Total Articles
38912
Total Views