diff --git a/src/app/app.component.html b/src/app/app.component.html index 63a2559..8016415 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -4,7 +4,7 @@ Browse Catalog Query Tool - + account_circle @@ -16,7 +16,7 @@ - + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 3e1804b..bfcf8e8 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -8,27 +8,27 @@ import { Subscription } from 'rxjs'; styleUrls: ['./app.component.scss'], }) export class AppComponent implements OnInit, OnDestroy { + isLoggedIn = false; username: string | null = null; + private loginStateSubscription?: Subscription; private usernameSubscription?: Subscription; constructor(private authService: AuthService) {} ngOnInit(): void { + this.loginStateSubscription = this.authService.isLoggedIn$.subscribe((status) => { + this.isLoggedIn = status; + }); + this.usernameSubscription = this.authService.username$.subscribe((username) => { this.username = username; }); - - if (this.isLoggedIn()) { - this.username = this.authService.getUsername(); - } } ngOnDestroy(): void { + this.loginStateSubscription?.unsubscribe(); this.usernameSubscription?.unsubscribe(); } - isLoggedIn(): boolean { - return this.authService.isLoggedIn(); - } logout() { this.authService.logout(); diff --git a/src/app/components/authentication/authentication.component.html b/src/app/components/authentication/authentication.component.html index e1c5b8c..6e72d56 100644 --- a/src/app/components/authentication/authentication.component.html +++ b/src/app/components/authentication/authentication.component.html @@ -1,4 +1,4 @@ - + Please login with a Catalog User. diff --git a/src/app/components/authentication/authentication.component.ts b/src/app/components/authentication/authentication.component.ts index 573e54a..749366b 100644 --- a/src/app/components/authentication/authentication.component.ts +++ b/src/app/components/authentication/authentication.component.ts @@ -1,27 +1,33 @@ -import { Component } from '@angular/core'; +import { Component, OnInit, OnDestroy } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { AuthService, TokenResponse } from 'src/app/services/auth.service'; +import { AuthService } from 'src/app/services/auth.service'; +import { Subscription } from 'rxjs'; @Component({ selector: 'app-authentication', templateUrl: './authentication.component.html', styleUrls: ['./authentication.component.scss'], }) -export class AuthenticationComponent { +export class AuthenticationComponent implements OnInit, OnDestroy { username = ''; password = ''; + isLoggedIn = false; + private loginStateSubscription?: Subscription; constructor(private authService: AuthService, private snackBar: MatSnackBar) {} - isLoggedIn(): boolean { - return this.authService.isLoggedIn(); + ngOnInit(): void { + this.loginStateSubscription = this.authService.isLoggedIn$.subscribe((status) => { + this.isLoggedIn = status; + }); + } + + ngOnDestroy(): void { + this.loginStateSubscription?.unsubscribe(); } login(): void { this.authService.login(this.username, this.password).subscribe({ - next: (response: TokenResponse) => { - this.authService.handleTokenResponse(response); - }, error: () => { this.showErrorMessage('Wrong username or password.'); }, diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index 1e046bd..2482725 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable, BehaviorSubject, catchError, of, tap } from 'rxjs'; declare global { @@ -22,8 +22,12 @@ export class AuthService { public readonly fcKeycloakClientScope: string; public readonly fcKeycloakClientId: string; public readonly fcKeycloakClientSecret: string; + private demoUsername: string | undefined; + private demoPassword: string | undefined; private usernameSubject: BehaviorSubject = new BehaviorSubject(null); username$: Observable = this.usernameSubject.asObservable(); + private isLoggedInSubject: BehaviorSubject = new BehaviorSubject(false); + isLoggedIn$: Observable = this.isLoggedInSubject.asObservable(); accessToken = ''; refreshToken = ''; expiresIn = 0; @@ -36,6 +40,8 @@ export class AuthService { this.fcKeycloakClientScope = window.ENVIRONMENT?.['FC_KEYCLOAK_CLIENT_SCOPE'] || 'gaia-x'; this.fcKeycloakClientId = window.ENVIRONMENT?.['FC_KEYCLOAK_CLIENT_ID'] || 'federated-catalogue'; this.fcKeycloakClientSecret = window.ENVIRONMENT?.['FC_KEYCLOAK_CLIENT_SECRET'] || 'keycloak-secret'; + this.demoUsername = window.ENVIRONMENT?.['DEMO_USERNAME']; + this.demoPassword = window.ENVIRONMENT?.['DEMO_PASSWORD']; const storedUsername = localStorage.getItem('username'); const storedToken = localStorage.getItem('accessToken'); const storedRefreshToken = localStorage.getItem('refreshToken'); @@ -50,29 +56,21 @@ export class AuthService { const elapsed = (Date.now() - storedTime) / 1000; this.expiresIn = expiresIn - elapsed; if (this.expiresIn > 0) { + this.isLoggedInSubject.next(true); this.startTokenExpirationTimer(); } else { this.refreshAccessToken(); } + } else if (this.demoUsername && this.demoPassword) { + this.loginWithDemoCredentials(); } else { - // Log in per env variables for demo purposes - const demoUsername = window.ENVIRONMENT?.['DEMO_USERNAME']; - const demoPassword = window.ENVIRONMENT?.['DEMO_PASSWORD']; - - if (demoUsername && demoPassword) { - this.login(demoUsername, demoPassword).subscribe((response) => { - this.handleTokenResponse(response); - }); - } + this.logout(); } } - isLoggedIn(): boolean { - return !!this.accessToken; - } - login(username: string, password: string): Observable { this.usernameSubject.next(username); + const body = new URLSearchParams(); body.set('scope', this.fcKeycloakClientScope); body.set('grant_type', 'password'); @@ -85,7 +83,27 @@ export class AuthService { 'Content-Type': 'application/x-www-form-urlencoded', }); - return this.http.post(this.fcKeycloakAuthUrl, body.toString(), { headers }); + return this.http + .post(this.fcKeycloakAuthUrl, body.toString(), { headers }) + .pipe( + tap((response) => { + this.handleTokenResponse(response); + this.isLoggedInSubject.next(true); + }), + ); + } + + loginWithDemoCredentials(): void { + if (this.demoUsername && this.demoPassword) { + this.login(this.demoUsername, this.demoPassword).subscribe({ + next: (response) => { + this.handleTokenResponse(response); + }, + error: () => { + this.logout(); + }, + }); + } } handleTokenResponse(response: TokenResponse): void { @@ -124,20 +142,17 @@ export class AuthService { this.http .post(this.fcKeycloakAuthUrl, body.toString(), { headers }) .pipe( - tap((response: TokenResponse) => this.handleTokenResponse(response)), - catchError((error: HttpErrorResponse) => { - console.error('Failed to refresh token', error); - this.logout(); + tap((response) => { + this.handleTokenResponse(response); + }), + catchError(() => { + this.loginWithDemoCredentials(); return of(null); }), ) .subscribe(); } - getUsername(): string | null { - return this.usernameSubject.value; - } - logout(): void { this.accessToken = ''; this.refreshToken = ''; @@ -151,5 +166,6 @@ export class AuthService { localStorage.removeItem('tokenStoredTime'); localStorage.removeItem('username'); this.usernameSubject.next(null); + this.isLoggedInSubject.next(false); } }
Please login with a Catalog User.