diff --git a/apps/client/src/app/app-routing.module.ts b/apps/client/src/app/app-routing.module.ts index 51dcaf4f5..d9c395f7a 100644 --- a/apps/client/src/app/app-routing.module.ts +++ b/apps/client/src/app/app-routing.module.ts @@ -97,6 +97,13 @@ const routes: Routes = [ loadChildren: () => import('./pages/zen/zen-page.module').then((m) => m.ZenPageModule) }, + { + path: 'webauthn', + loadChildren: () => + import('./pages/webauthn/webauthn-page.module').then( + (m) => m.WebauthnPageModule + ) + }, { // wildcard, if requested url doesn't match any paths for routes defined // earlier diff --git a/apps/client/src/app/components/header/header.component.html b/apps/client/src/app/components/header/header.component.html index 9d566f7de..a85e2636a 100644 --- a/apps/client/src/app/components/header/header.component.html +++ b/apps/client/src/app/components/header/header.component.html @@ -1,4 +1,9 @@ + + + + + diff --git a/apps/client/src/app/core/http-response.interceptor.ts b/apps/client/src/app/core/http-response.interceptor.ts index 3d807bfd3..50489d10a 100644 --- a/apps/client/src/app/core/http-response.interceptor.ts +++ b/apps/client/src/app/core/http-response.interceptor.ts @@ -79,10 +79,7 @@ export class HttpResponseInterceptor implements HttpInterceptor { } } else if (error.status === StatusCodes.UNAUTHORIZED) { if (this.webAuthnService.isEnabled()) { - this.webAuthnService.login().subscribe(({ authToken }) => { - this.tokenStorageService.saveToken(authToken, false); - window.location.reload(); - }); + this.router.navigate(['/webauthn']); } else { this.tokenStorageService.signOut(); } diff --git a/apps/client/src/app/pages/webauthn/webauthn-page-routing.module.ts b/apps/client/src/app/pages/webauthn/webauthn-page-routing.module.ts new file mode 100644 index 000000000..35bfb2580 --- /dev/null +++ b/apps/client/src/app/pages/webauthn/webauthn-page-routing.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { WebauthnPageComponent } from '@ghostfolio/client/pages/webauthn/webauthn-page.component'; + +const routes: Routes = [{ path: '', component: WebauthnPageComponent }]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class WebauthnPageRoutingModule {} diff --git a/apps/client/src/app/pages/webauthn/webauthn-page.component.ts b/apps/client/src/app/pages/webauthn/webauthn-page.component.ts new file mode 100644 index 000000000..38ce6f621 --- /dev/null +++ b/apps/client/src/app/pages/webauthn/webauthn-page.component.ts @@ -0,0 +1,46 @@ +import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; +import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'gf-webauthn-page', + templateUrl: './webauthn-page.html', + styleUrls: ['./webauthn-page.scss'] +}) +export class WebauthnPageComponent implements OnInit { + public hasError = false; + + constructor( + private changeDetectorRef: ChangeDetectorRef, + private router: Router, + private tokenStorageService: TokenStorageService, + private webAuthnService: WebAuthnService + ) {} + + ngOnInit(): void { + this.signIn(); + } + + public deregisterDevice() { + this.webAuthnService + .deregister() + .subscribe(() => this.router.navigate([''])); + } + + public signIn() { + this.hasError = false; + + this.webAuthnService.login().subscribe( + ({ authToken }) => { + this.tokenStorageService.saveToken(authToken, false); + this.router.navigate(['']); + }, + (error) => { + console.error(error); + this.hasError = true; + this.changeDetectorRef.markForCheck(); + } + ); + } +} diff --git a/apps/client/src/app/pages/webauthn/webauthn-page.html b/apps/client/src/app/pages/webauthn/webauthn-page.html new file mode 100644 index 000000000..1c12f1fa0 --- /dev/null +++ b/apps/client/src/app/pages/webauthn/webauthn-page.html @@ -0,0 +1,24 @@ +
+
+
+ +
+
+

+ Authentication failed +

+ + +
+
+
diff --git a/apps/client/src/app/pages/webauthn/webauthn-page.module.ts b/apps/client/src/app/pages/webauthn/webauthn-page.module.ts new file mode 100644 index 000000000..e4e392400 --- /dev/null +++ b/apps/client/src/app/pages/webauthn/webauthn-page.module.ts @@ -0,0 +1,20 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; + +import { WebauthnPageRoutingModule } from './webauthn-page-routing.module'; +import { WebauthnPageComponent } from '@ghostfolio/client/pages/webauthn/webauthn-page.component'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatButtonModule } from '@angular/material/button'; + +@NgModule({ + declarations: [WebauthnPageComponent], + exports: [], + imports: [ + WebauthnPageRoutingModule, + CommonModule, + MatButtonModule, + MatProgressSpinnerModule + ], + providers: [] +}) +export class WebauthnPageModule {} diff --git a/apps/client/src/app/pages/webauthn/webauthn-page.scss b/apps/client/src/app/pages/webauthn/webauthn-page.scss new file mode 100644 index 000000000..e69de29bb