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

@Component({
  selector: 'app-root',
  template: `<app-test-auth></app-test-auth>`
})
export class AppComponent { }
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {HttpClientModule} from '@angular/common/http';
import {HTTP_INTERCEPTORS} from '@angular/common/http';
import {RouterModule} from '@angular/router';

import {AuthModule} from '@juztcode/angular-auth';
import {AppComponent} from './app.component';
import {TestAuthComponent} from './components/test-auth/test-auth.component';
import {RequestLoggerInterceptor} from './providers/request-logger/request-logger.interceptor';

import {AUTH_CONFIG, AUTH_CONFIG_ADDITIONAL} from './config/auth-package.config';

@NgModule({
  declarations: [
    AppComponent,
    TestAuthComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    AuthModule.forRoot(AUTH_CONFIG, AUTH_CONFIG_ADDITIONAL)
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: RequestLoggerInterceptor,
      multi: true
    }  
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}
import {Component, OnInit} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {AuthProvider, PermissionProvider} from '@juztcode/angular-auth';

@Component({
  selector: 'app-test-auth',
  templateUrl: './test-auth.component.html',
  styles: []
})
export class TestAuthComponent implements OnInit {

  userRole = 0;
  accessTokenCountDown = 0;
  refreshTokenCountDown = 0;
  permissions: any = {};

  constructor(private authProvider: AuthProvider, private permissionProvider: PermissionProvider, private http: HttpClient) {
  }

  ngOnInit() {
    this.startTimer();
    this.getTimeRemaining();
    this.isLoggedIn();
    this.permissions = this.permissionProvider.getPermissions();
  }

  startTimer() {
    setInterval(() => {
      if (this.accessTokenCountDown > 0) {
        this.accessTokenCountDown--;
      }

      if (this.refreshTokenCountDown > 0) {
        this.refreshTokenCountDown--;
      }
    }, 1000);
  }

  setTimeRemaining(accessTokenCountDown: number, refreshTokenCountDown: number) {
    const now = Math.floor(Date.now() / 1000);
    this.accessTokenCountDown = accessTokenCountDown;
    this.refreshTokenCountDown = refreshTokenCountDown;
    localStorage.setItem('access-token-expired-time', (now + accessTokenCountDown).toString());
    localStorage.setItem('refresh-token-expired-time', (now + refreshTokenCountDown).toString());
  }

  getTimeRemaining() {
    const now = Math.floor(Date.now() / 1000);
    const accessTokenExpiredTime = localStorage.getItem('access-token-expired-time');
    const refreshTokenExpiredTime = localStorage.getItem('refresh-token-expired-time');

    if (!accessTokenExpiredTime || !refreshTokenExpiredTime) {
      return;
    }

    const accessTokenTimeRemaining = +accessTokenExpiredTime - now;
    const refreshTokenTimeRemaining = +refreshTokenExpiredTime - now;

    if (accessTokenTimeRemaining > 0) {
      this.accessTokenCountDown = accessTokenTimeRemaining;
    }
    if (refreshTokenTimeRemaining > 0) {
      this.refreshTokenCountDown = refreshTokenTimeRemaining;
    }
  }

  async isLoggedIn() {
    const status = await this.authProvider.isLoggedIn();
    if (status) {
      this.userRole = this.authProvider.getValueInToken<number>('role');
    } else {
      this.userRole = 0;
    }
    console.log('Login status:', status);
  }

  async sendLogin(username: string, password: string) {
    try{
      await this.authProvider.login({username: username, password: password}); 
      this.setTimeRemaining(60, 60 * 5);
    }catch (e) {
      console.error('login failed');
      this.setTimeRemaining(0, 0);
      throw e;
    }
    await this.isLoggedIn();
  }

  async getSecuredResource() {
    try {
      const result = await this.http.get<any>('https://test-angular-packages.herokuapp.com/secured').toPromise();
      console.log('secured route accessed: ' + JSON.stringify(result));
      if (this.accessTokenCountDown === 0) {
        this.setTimeRemaining(60, 60 * 5);
      }
    } catch (e) {
      console.error('secured route access failed')
      this.setTimeRemaining(0, 0);
      throw e;
    }
  }

  async logOut() {
    await this.authProvider.logOut();
    await this.isLoggedIn();
    this.setTimeRemaining(0, 0);
  }

}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="#">Simple Angular Jwt Auth - Sample Project</a>
</nav>
<div style="padding: 25px;">
  <div class="row">
    <div class="col-sm-6">
      <form style="padding: 25px; border: 1px solid rgba(0, 0, 0, 0.1);">
        <div class="form-group row">
          <label class="col-sm-4 col-form-label" for="usernameInputArea">Username</label>
          <div class="col-sm-8">
            <input #username type="text" class="form-control" id="usernameInputArea" placeholder="user-1 or user-2">
          </div>
        </div>
        <div class="form-group row">
          <label class="col-sm-4 col-form-label" for="passwordInputArea">Password</label>
          <div class="col-sm-8">
            <input #password type="password" class="form-control" id="passwordInputArea" placeholder="1234">
          </div>
        </div>
        <div class="form-group row">
          <div class="col-sm-12">
            <button type="button" class="btn btn-primary form-control" (click)="sendLogin(username.value, password.value)">Login</button>
          </div>
        </div>
        <div class="form-group row">
          <div class="col-sm-12">
            <button type="button" class="btn btn-info form-control" (click)="isLoggedIn()">Is Logged In</button>
          </div>
        </div>
        <div class="form-group row">
          <div class="col-sm-12">
            <button type="button" class="btn btn-warning form-control" (click)="getSecuredResource()">Get Secured</button>
          </div>
        </div>
        <div class="form-group row">
          <div class="col-sm-12">
            <button type="button" class="btn btn-danger form-control" (click)="logOut()">LogOut</button>
          </div>
        </div>
      </form>
      <div style="padding: 25px; border: 1px solid rgba(0, 0, 0, 0.1);">
        <div class="row">
          <div class="col-sm-6">
            <h5>User Role is: {{userRole}}</h5>
          </div>
          <div class="col-sm-6">
            <h5>permissions.canEdit: {{permissions.canEdit}}</h5>
          </div>
        </div>
      </div>
    </div>
    <div class="col-sm-6">
      <div style="padding: 25px; border: 1px solid rgba(0, 0, 0, 0.1);">
        <h3>Time to expire access token
          <span class="badge badge-secondary">{{accessTokenCountDown}} seconds</span> 
        </h3>
        <h3>Time to expire refresh token
          <span class="badge badge-secondary">{{refreshTokenCountDown}} seconds</span>
        </h3>
      </div>
      <div id="debugView" style="padding: 25px; border: 1px solid rgba(0, 0, 0, 0.1); height: 320px; overflow-y:scroll;">

      </div>
    </div>
  </div>
</div>
import {
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest,
  HttpResponse,
  HttpErrorResponse
} from "@angular/common/http";
import { Observable } from "rxjs/Observable";
import "rxjs/add/operator/do";
import { Injectable } from "@angular/core";

import { LOGIN_URL, REFRESH_URL, SECURED_URL } from "../../config/api.config";

@Injectable()
export class RequestLoggerInterceptor implements HttpInterceptor {
  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return next.handle(req).do((event: HttpEvent<any>) => {
      if (event instanceof HttpRequest) {
        switch (req.url) {
          case LOGIN_URL:
            console.log("login request is sent");
            console.log("request body: ", req.body);
            break;
          case REFRESH_URL:
            console.log("refresh token request is sent");
            break;
          case SECURED_URL:
            console.log("secured request sent");
        }
      }
      if (event instanceof HttpResponse && !(event instanceof HttpErrorResponse)) {
        switch (req.url) {
          case LOGIN_URL:
            console.log("auth received: ", event.body);
            break;
          case REFRESH_URL:
            console.log("auth received: ", event.body);
            break;
          case SECURED_URL:
            console.log("authorization: ", req.headers.get("Authorization"));
        }
      }
    });
  }
}
import {AuthConfig, AuthConfigAdditional} from '@juztcode/angular-auth';
import {LOGIN_URL, REFRESH_URL} from './api.config';

export const AUTH_CONFIG: AuthConfig = {
  persistTokensEnabled: true,
  refreshTokenEnabled: true,
  userPermissionsEnabled: true,
  loginUrl: LOGIN_URL,
  refreshTokenUrl: REFRESH_URL,
  permissionDataSet: [
    {userRoleId: 1, permissions: {canEdit: true}},
    {userRoleId: 2, permissions: {canEdit: false}}
  ],
  accessTokenExpiredResponseStatus: 403,
  accessTokenExpiredErrorCode: 4001,
  refreshTokenExpiredResponseStatus: 403,
  refreshTokenExpiredErrorCode: 4002
};

export const AUTH_CONFIG_ADDITIONAL: AuthConfigAdditional = {
  userRoleIdKey: 'role',
  globalHttpHeaders: [{key: 'Content-Type', value: 'application/json', excludedMethods: ['GET']}]
};
const HOST = 'https://hook.io/randilfernando';

export const LOGIN_URL = `${HOST}/angular-test-login`;
export const REFRESH_URL = `${HOST}/angular-test-refresh`;
export const SECURED_URL = `${HOST}/angular-test-secured`;
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule);
<!doctype html>
<html lang="en">

<head>
  <title>@juztcode/angular-auth sample project</title>
  <script>
    document.write('<base href="' + document.location + '" />');
  </script>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/css/bootstrap.min.css" integrity="sha384-Zug+QiDoJOrZ5t4lssLdxGhVrurbmBWopoEl+M6BdEfwnCJZtKxi1KgxUyJq13dy"
    crossorigin="anonymous">
  <!-- Polyfills -->
  <script src="https://unpkg.com/core-js/client/shim.min.js"></script>
  <script src="https://unpkg.com/zone.js@0.7.4?main=browser"></script>
  <script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/juztcode/console-to-html@0.0.1/src/console-to-html.js"></script>
  <script src="systemjs.config.js"></script>
  <script>
    System.import('main.ts').catch(function (err) {
      console.error(err);
    });
    
    var debugToHTML = new DebugToHTML('debugView');
  </script>
</head>

<body>
  <app-root></app-root>
</body>

</html>
/**
 * WEB ANGULAR VERSION
 * (based on systemjs.config.js in angular.io)
 * System configuration for Angular samples
 * Adjust as necessary for your application needs.
 */
(function (global) {
  System.config({
    // DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
    transpiler: 'ts',
    typescriptOptions: {
      // Copy of compiler options in standard tsconfig.json
      "target": "es5",
      "module": "system",
      "moduleResolution": "node",
      "sourceMap": true,
      "emitDecoratorMetadata": true,
      "experimentalDecorators": true,
      "lib": ["es2015", "dom"],
      "noImplicitAny": true,
      "suppressImplicitAnyIndexErrors": true
    },
    meta: {
      'typescript': {
        "exports": "ts"
      }
    },
    paths: {
      // paths serve as alias
      'npm:': 'https://unpkg.com/'
    },
    // map tells the System loader where to look for things
    map: {
      // our app is within the app folder
      'app': 'app',

      // angular bundles
      '@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
      '@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
      '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
      '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
      '@angular/common/http': 'npm:@angular/common/bundles/common-http.umd.js',
      '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
      '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
      '@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
      '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
      '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
      '@angular/router/upgrade': 'npm:@angular/router/bundles/router-upgrade.umd.js',
      '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',

      // other libraries
      'rxjs':                      'npm:rxjs@5.5.2',
      'rxjs/operators':            'npm:rxjs@5.5.2/operators/index.js',
      'tslib':                     'npm:tslib/tslib.js',
      'ts':                        'npm:plugin-typescript@5.2.7/lib/plugin.js',
      'typescript':                'npm:typescript@2.4.2/lib/typescript.js',
      
      // additional libraries
      '@juztcode/angular-auth':   'npm:@juztcode/angular-auth/bundles/angular-auth.umd.js'

    },
    // packages tells the System loader how to load when no filename and/or no extension
    packages: {
      app: {
        main: './main.ts',
        defaultExtension: 'ts',
        meta: {
          './*.ts': {
            loader: 'systemjs-angular-loader.js'
          }
        }
      },
      rxjs: {
        defaultExtension: 'js'
      }
    }
  });

})(this);

/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm;
var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g;
var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g;

module.exports.translate = function(load){
  if (load.source.indexOf('moduleId') != -1) return load;

  var url = document.createElement('a');
  url.href = load.address;

  var basePathParts = url.pathname.split('/');

  basePathParts.pop();
  var basePath = basePathParts.join('/');

  var baseHref = document.createElement('a');
  baseHref.href = this.baseURL;
  baseHref = baseHref.pathname;

  if (!baseHref.startsWith('/base/')) { // it is not karma
    basePath = basePath.replace(baseHref, '');
  }

  load.source = load.source
    .replace(templateUrlRegex, function(match, quote, url){
      var resolvedUrl = url;

      if (url.startsWith('.')) {
        resolvedUrl = basePath + url.substr(1);
      }

      return 'templateUrl: "' + resolvedUrl + '"';
    })
    .replace(stylesRegex, function(match, relativeUrls) {
      var urls = [];

      while ((match = stringRegex.exec(relativeUrls)) !== null) {
        if (match[2].startsWith('.')) {
          urls.push('"' + basePath + match[2].substr(1) + '"');
        } else {
          urls.push('"' + match[2] + '"');
        }
      }

      return "styleUrls: [" + urls.join(', ') + "]";
    });

  return load;
};
#@juztcode/angular-auth

This is the demo project to test the functionality of @juztcode/angular-auth.

Open developer tools > console to view the log

To view how to add library to your project click [here](https://www.npmjs.com/package/@juztcode/angular-auth)