2.0.1 • Published 1 month ago

ngboost-auth v2.0.1

Weekly downloads
-
License
MIT
Repository
-
Last release
1 month ago

ngboost-auth

Angular module for Json Web Token (JWT) authentication. The module methods are used with Angular router.

Installation

$ npm install --save ngboost-auth

Features

  • AuthService - use in component: login, logout, getLoggedUserInfo, getJWTtoken
  • JwtTokenInterceptor - intercept every request of the httpClient and add header Authorization: JWT ...
  • IsLoggedService route guard - check if the user is logged succesfully, if not return to afterBadLogin
  • HasRoleService route guard - check if the logged user has valid role (admin,customer,...), if not return to afterBadLogin
  • AutologinService route guard - if the user is logged redirect from /login route to afterGoodLogin route

AuthService Methods

login(creds: Credentials): Observable

Login with const creds = {username: 'xxxx', password: 'yyyy'} and redirect to URL defined by auth_urls.afterGoodLogin.

Required 'creds' properties are:

interface Credentials {
  username: string;
  password: string;
}

logout(): void

Logout and remove cookies.

getLoggedUserInfo(): LoggedUser

Get logged user info. This object is returned by API and stored into cookie auth_loggedUser. Set auth_jwtToken and auth_loggedUser cookies.

Required user object properties are:

interface LoggedUser {
  first_name: string;
  last_name: string;
  address?: string;
  city?: string;
  country?: string;

  phone?: string;
  email: string;
  website?: string;

  username: string;
  password?: string;

  role: string;
  is_active?: boolean;
}

so API response should return those fields.

getJWTtoken(): string

Take JWT token from 'auth_jwtToken' cookie. This token is fetched from API and stored in 'auth_jwtToken' cookie. Remove auth_jwtToken and auth_loggedUser ccokies.

Integration

1. app.module.ts

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgboostAuthModule } from 'ngboost-auth';
import { AppRoutingModule } from './app-routing.module';

const api_base_url = 'http://localhost:4444';

const auth_urls = {
  apiLoginUrl: `${api_base_url}/users/login`,
  afterGoodLogin: '/{loggedUserRole}/dashboard', // {loggedUserRole} -> 'admin' | 'customer'
  afterBadLogin: '/login',
  afterLogout: '/login'
};

@NgModule({
  declarations: [],
  imports: [ AppRoutingModule, NgboostAuthModule, HttpClientModule ],
  providers: [
     // required injections for ngboost-auth
    { provide: 'AUTH_URLS', useValue: AUTH_URLS }, // send AUTH_URLS to ngboost-auth
    { provide: HTTP_INTERCEPTORS, useClass: JwtTokenInterceptor, multi: true }, // take JwtTokenInterceptor from ngboost-auth and send to HttpClientModule
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

Notice: {loggedUserRole} is a placeholder which is replaced with user's role: admin, customer, ...etc. User's role have to be returned by API after succesful login.

2. app-routing.module.ts

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

// components
import { HomeComponent } from './pages/home/home.component';
import { LoginComponent } from './pages/login/login.component';
import { AdminComponent } from './pages/admin/admin.component';
import { CustomerComponent } from './pages/customer/customer.component';
import { NotfoundComponent } from './pages/notfound/notfound.component';

// services
import { IsLoggedService, HasRoleService, AutologinService } from 'ngboost-auth';

// routes
const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'login', component: LoginComponent, canActivate: [AutologinService] },
  { path: 'admin', component: AdminComponent, canActivate: [IsLoggedService, HasRoleService] },
  { path: 'customer', component: CustomerComponent, canActivate: [IsLoggedService, HasRoleService] },
  { path: '404', component: NotfoundComponent },
  { path: '**', redirectTo: '/404' }
];


@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  providers: [IsLoggedService, HasRoleService, AutologinService],
  exports: [ RouterModule ],
  declarations: [
    HomeComponent,
    LoginComponent,
    AdminComponent,
    CustomerComponent,
    NotfoundComponent
  ]
})

export class AppRoutingModule {}

Backend API response

In order to work properly this ng module requires this kind of JSON response from the API server:

{
    "success": true,
    "message": "Login was successful. JWT is generated and you can use it in API request header. Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVhZjdmYTZmMjlmOWJlMTg1MmRkNjMyZSIsInVzZXJuYW1lIjoiYWRtaW4iLCJpYXQiOjE1Mjc5MzYxNjR9.QI78esEzZkxpkuMhWGqPASGvRvrti1GNWM7ozxnvyfU",
    "jwtToken": "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVhZjdmYTZmMjlmOWJlMTg1MmRkNjMyZSIsInVzZXJuYW1lIjoiYWRtaW4iLCJpYXQiOjE1Mjc5MzYxNjR9.QI78esEzZkxpkuMhWGqPASGvRvrti1GNWM7ozxnvyfU",
    "loggedUser": {
        "role": "admin",
        "is_active": true,
        "_id": "5af7fa6f29f9be1852dd632e",
        "first_name": "Marko",
        "last_name": "Adminić",
        "address": "Radića 23",
        "city": "Osijek",
        "country": "Croatia",
        "phone": "+385-93-2111-222",
        "email": "test@uniapi.com",
        "website": "www.uniapi.org",
        "username": "admin",
        "created_at": "2018-05-13T08:42:23.363Z",
        "updated_at": "2018-06-02T10:42:44.175Z",
        "__v": 0,
        "jwt_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVhZjdmYTZmMjlmOWJlMTg1MmRkNjMyZSIsInVzZXJuYW1lIjoiYWRtaW4iLCJpYXQiOjE1Mjc5MzYxNjR9.QI78esEzZkxpkuMhWGqPASGvRvrti1GNWM7ozxnvyfU"
    }
}

AuthService Method Examples

login({username: , password: })

/pages/login/login.component.html

<form id="login-form" action="#" method="POST" novalidate="" [formGroup]="loginFG">
    <div class="form-group">
        <label for="username">Username</label>
        <input type="text" class="form-control underlined" placeholder="Your username" required formControlName="username"> </div>
    <div class="form-group">
        <label for="password">Password</label>
        <input type="password" class="form-control underlined" placeholder="Your password" required formControlName="password"> </div>
    <div class="form-group">
        <button type="button" class="btn btn-block btn-primary" (click)="login()">Login</button>
    </div>
    <div>
        <p class="alert alert-warning" *ngIf="!!err && !!err.message">{{err.message}}</p>
    </div>
</form>

/pages/login/login.component.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { AuthService } from 'ngboost-auth';


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

  loginFG: FormGroup;
  err: Error;

  constructor(private fb: FormBuilder, private authService: AuthService) { }

  ngOnInit() {
    this.loginFG = this.fb.group({
      username: '',
      password: ''
    });
  }


  login() {
    const creds = this.loginFG.value; // {username: , password: }
    this.authService.login(creds)
      .subscribe({
        next: (apiResp: any) => {
          const jwtToken = this.authService.getJWTtoken();
          console.info('LOGGED USER:: ', apiResp.loggedUser, ' jwtToken=', jwtToken);
          this.msg = apiResp.msg;
        },
        error: err => {
          this.errMsg = err.error.message;
          setTimeout(() => { this.errMsg = ''; }, 2100);
          console.error('ERROR: ', err);
        }
      });
  }

}

logout()

/pages/admin/admin.component.html

<a class="dropdown-item" [routerLink]="" (click)="logout()"><i class="fa fa-power-off icon"></i> Logout </a>

/pages/admin/admin.component.ts

import { Component, OnInit } from '@angular/core';
import { AuthService } from 'ngboost-auth';

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

  loggedUser: any;

  constructor(private authService: AuthService) {
    this.loggedUser = authService.getLoggedUserInfo();
  }

  ngOnInit() {
  }

  logout() {
    console.log('LOGOUT:: ');
    this.authService.logout();
  }

}

Protections

A] Route Guards

To protect angular routes from unauthorized access this service uses canActivate route guards:

  • IsLoggedService check if user is logged with valid username:password.
  • HasRoleService check if user has appropriate role, e.g. role must be contained in URL string, for example: /admin/dashboard is valid for 'admin' role but /customer/dashboard is not valid for 'admin' role. This guard must be applied after IsLoggedService guard.
  • AutologinService check if user is already logged. If yes then automatically redirect to URL defined by auth_urls.afterGoodLogin. This guard should be applied only on login page e.g. /login angular route.

B] API HTTP Request protections

To protect each API request use JWT Token from 'auth_jwtToken' cookie and use it in HTTP header 'Authorization: JWT ' as HTTP interceptor for each API request.

Licence

Created by Saša Mikodanić under MIT licence.

1.0.1

1 month ago

2.0.1

1 month ago

2.0.0

1 month ago

1.0.0

1 year ago