From ac4ff602aee2253e5e3aced4bb38b479fd81db17 Mon Sep 17 00:00:00 2001 From: Wolfgang Ludger Hottgenroth Date: Fri, 18 Jun 2021 18:24:01 +0200 Subject: [PATCH] redirect after login works now --- hv2-ui/package-lock.json | 5 ++++ hv2-ui/package.json | 1 + hv2-ui/src/app/app-routing.module.ts | 3 +- hv2-ui/src/app/auth-guard.service.spec.ts | 16 ++++++++++ hv2-ui/src/app/auth-guard.service.ts | 22 ++++++++++++++ hv2-ui/src/app/error-handler.interceptor.ts | 2 +- hv2-ui/src/app/login/login.component.ts | 6 +++- hv2-ui/src/app/logout/logout.component.ts | 7 +++-- .../app/navigation/navigation.component.ts | 9 +++++- hv2-ui/src/app/token.service.ts | 30 ++++++++++++++----- 10 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 hv2-ui/src/app/auth-guard.service.spec.ts create mode 100644 hv2-ui/src/app/auth-guard.service.ts diff --git a/hv2-ui/package-lock.json b/hv2-ui/package-lock.json index 7a9ee03..9eb7b46 100644 --- a/hv2-ui/package-lock.json +++ b/hv2-ui/package-lock.json @@ -6725,6 +6725,11 @@ "set-immediate-shim": "~1.0.1" } }, + "jwt-decode": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" + }, "karma": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/karma/-/karma-5.1.1.tgz", diff --git a/hv2-ui/package.json b/hv2-ui/package.json index 221a324..786d7ef 100644 --- a/hv2-ui/package.json +++ b/hv2-ui/package.json @@ -21,6 +21,7 @@ "@angular/platform-browser": "~11.0.6", "@angular/platform-browser-dynamic": "~11.0.6", "@angular/router": "~11.0.6", + "jwt-decode": "^3.1.2", "rxjs": "~6.6.0", "tslib": "^2.0.0", "zone.js": "~0.10.2" diff --git a/hv2-ui/src/app/app-routing.module.ts b/hv2-ui/src/app/app-routing.module.ts index 0738370..dad59ee 100644 --- a/hv2-ui/src/app/app-routing.module.ts +++ b/hv2-ui/src/app/app-routing.module.ts @@ -1,5 +1,6 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { AuthGuardService } from './auth-guard.service'; import { LoginComponent } from './login/login.component'; import { LogoutComponent } from './logout/logout.component'; import { TestOutputComponent } from './test-output/test-output.component'; @@ -7,7 +8,7 @@ import { TestOutputComponent } from './test-output/test-output.component'; const routes: Routes = [ - { path: 'test', component: TestOutputComponent }, + { path: 'test', component: TestOutputComponent, canActivate: [ AuthGuardService ] }, { path: 'logout', component: LogoutComponent }, { path: 'login', component: LoginComponent } ] diff --git a/hv2-ui/src/app/auth-guard.service.spec.ts b/hv2-ui/src/app/auth-guard.service.spec.ts new file mode 100644 index 0000000..35afd37 --- /dev/null +++ b/hv2-ui/src/app/auth-guard.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthGuardService } from './auth-guard.service'; + +describe('AuthGuardService', () => { + let service: AuthGuardService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AuthGuardService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/hv2-ui/src/app/auth-guard.service.ts b/hv2-ui/src/app/auth-guard.service.ts new file mode 100644 index 0000000..f1c6ae7 --- /dev/null +++ b/hv2-ui/src/app/auth-guard.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { MessageService } from './message.service'; +import { TokenService } from './token.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthGuardService implements CanActivate { + + constructor(public tokenService: TokenService, public router: Router, private messageService: MessageService) { + } + + canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + this.messageService.add(`Activated: ${next.url}`) + if (! this.tokenService.checkAuthenticated()) { + return this.router.parseUrl(`/login?returnUrl=${next.url}`) + } else { + return true; + } + } +} diff --git a/hv2-ui/src/app/error-handler.interceptor.ts b/hv2-ui/src/app/error-handler.interceptor.ts index 3e3f925..2cdca84 100644 --- a/hv2-ui/src/app/error-handler.interceptor.ts +++ b/hv2-ui/src/app/error-handler.interceptor.ts @@ -21,7 +21,7 @@ export class ErrorHandlerInterceptor implements HttpInterceptor { catchError((errorResponse: HttpErrorResponse) => { this.messageService.add(`Intercepted http error: ${JSON.stringify(errorResponse, undefined, 4)}`) if (errorResponse.status === 401) { - this.router.navigateByUrl('/login') + this.router.navigate(['login']) } return throwError(errorResponse) }) diff --git a/hv2-ui/src/app/login/login.component.ts b/hv2-ui/src/app/login/login.component.ts index 4164e0b..0a2a007 100644 --- a/hv2-ui/src/app/login/login.component.ts +++ b/hv2-ui/src/app/login/login.component.ts @@ -22,7 +22,7 @@ export class LoginComponent implements OnInit { private tokenService: TokenService, private messageService: MessageService) { this.returnUrl = this.route.snapshot.queryParams.returnUrl || '/' - + this.messageService.add(`Return URL is ${this.returnUrl}`) this.form = this.fb.group({ username: ['', Validators.required], password: ['', Validators.required] @@ -30,6 +30,9 @@ export class LoginComponent implements OnInit { } ngOnInit(): void { + if (this.tokenService.checkAuthenticated()) { + this.router.navigate([this.returnUrl]) + } } async onSubmit(): Promise { @@ -40,6 +43,7 @@ export class LoginComponent implements OnInit { const username = this.form.get('username')?.value; const password = this.form.get('password')?.value; await this.tokenService.login(username, password); + this.router.navigate([this.returnUrl]) } catch (err) { this.messageService.add(`Login err: ${err.message}`) this.loginInvalid = true; diff --git a/hv2-ui/src/app/logout/logout.component.ts b/hv2-ui/src/app/logout/logout.component.ts index 0a7c2fa..7f0dd44 100644 --- a/hv2-ui/src/app/logout/logout.component.ts +++ b/hv2-ui/src/app/logout/logout.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; import { MessageService } from '../message.service'; import { TokenService } from '../token.service'; @@ -9,11 +10,11 @@ import { TokenService } from '../token.service'; }) export class LogoutComponent implements OnInit { - constructor(private messageService: MessageService) { } + constructor(private tokenService: TokenService, private router: Router, private messageService: MessageService) { } ngOnInit(): void { - localStorage.removeItem(TokenService.Id_Token_Key) - this.messageService.add("Token removed from local storage") + this.tokenService.logout() + this.router.navigateByUrl('/login') } } diff --git a/hv2-ui/src/app/navigation/navigation.component.ts b/hv2-ui/src/app/navigation/navigation.component.ts index 6419fb0..786b053 100644 --- a/hv2-ui/src/app/navigation/navigation.component.ts +++ b/hv2-ui/src/app/navigation/navigation.component.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; import { Observable } from 'rxjs'; import { map, shareReplay } from 'rxjs/operators'; +import { TokenService } from '../token.service'; @Component({ selector: 'app-navigation', @@ -10,12 +11,18 @@ import { map, shareReplay } from 'rxjs/operators'; }) export class NavigationComponent { + public authenticated: boolean + isHandset$: Observable = this.breakpointObserver.observe(Breakpoints.Handset) .pipe( map(result => result.matches), shareReplay() ); - constructor(private breakpointObserver: BreakpointObserver) {} + constructor(private breakpointObserver: BreakpointObserver, private tokenService: TokenService) {} + + ngOnInit() { + this.authenticated = this.tokenService.checkAuthenticated() + } } diff --git a/hv2-ui/src/app/token.service.ts b/hv2-ui/src/app/token.service.ts index c7661a0..f0b2fd3 100644 --- a/hv2-ui/src/app/token.service.ts +++ b/hv2-ui/src/app/token.service.ts @@ -2,7 +2,8 @@ import { Injectable } from '@angular/core'; import { MessageService } from './message.service'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { UserCreds } from './userCreds' -import { ThrowStmt } from '@angular/compiler'; +import jwt_decode from 'jwt-decode' + @Injectable({ providedIn: 'root' }) @@ -11,14 +12,28 @@ export class TokenService { public static Id_Token_Key : string = "id_token"; constructor(private http: HttpClient, private messageService: MessageService) { - this.messageService.add("TokenService started, token created") - - if (! localStorage.getItem(TokenService.Id_Token_Key)) { - localStorage.setItem(TokenService.Id_Token_Key, "abc") - this.messageService.add("Token set in local storage") - } } + checkAuthenticated(): boolean { + let result: boolean = false + + const token = localStorage.getItem(TokenService.Id_Token_Key) + if (token) { + let expiration = jwt_decode(token)["exp"] + if ((expiration * 1000) > Date.now()) { + result = true + } else { + this.logout() + } + } + + return result + } + + logout() { + localStorage.removeItem(TokenService.Id_Token_Key) + this.messageService.add("Token removed from local storage") + } async login(login: string, password: string) { this.messageService.add(`TokenService: trying to login and obtain token`); @@ -32,7 +47,6 @@ export class TokenService { userCreds, {responseType:'text'} ).toPromise() - this.messageService.add(`Token is ${token}`) localStorage.setItem(TokenService.Id_Token_Key, token) this.messageService.add("Token saved") }