diff --git a/client/src/app/app.config.ts b/client/src/app/app.config.ts
index fee4921..310ce60 100644
--- a/client/src/app/app.config.ts
+++ b/client/src/app/app.config.ts
@@ -3,6 +3,8 @@ import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { DATE_PIPE_DEFAULT_OPTIONS } from '@angular/common';
+import { provideHttpClient, withInterceptors } from '@angular/common/http';
+import { authInterceptor } from './services/auth-interceptor';
export const appConfig: ApplicationConfig = {
providers: [
@@ -10,5 +12,6 @@ export const appConfig: ApplicationConfig = {
provideRouter(routes),
{provide: DEFAULT_CURRENCY_CODE, useValue: ''},
{provide: DATE_PIPE_DEFAULT_OPTIONS, useValue: {dateFormat: 'shortDate'}},
+ provideHttpClient(withInterceptors([authInterceptor])),
]
};
diff --git a/client/src/app/screens/screen-login/screen-login.html b/client/src/app/screens/screen-login/screen-login.html
index c9462d1..2e75c76 100644
--- a/client/src/app/screens/screen-login/screen-login.html
+++ b/client/src/app/screens/screen-login/screen-login.html
@@ -57,15 +57,15 @@
+ @if (error) {
{{ error }}
-
+ }
diff --git a/client/src/app/screens/screen-login/screen-login.ts b/client/src/app/screens/screen-login/screen-login.ts
index 5d408b5..604d8b4 100644
--- a/client/src/app/screens/screen-login/screen-login.ts
+++ b/client/src/app/screens/screen-login/screen-login.ts
@@ -4,6 +4,7 @@ import { Validators, FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, F
import { ActivatedRoute, Router } from '@angular/router';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { APIService } from '../../services/api';
+import { GenericMessage } from '@message/Message';
@Component({
selector: 'app-screen-login',
@@ -40,9 +41,10 @@ export class ScreenLogin {
const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
this.router.navigateByUrl(returnUrl);
},
- error: (err) => {
+ error: (resp) => {
//FIXME error message displaying delayed, display message from server response
- this.error = err.error?.message || 'Login failed. Please try again.';
+ let msg: GenericMessage = resp.error;
+ this.error = msg.message || 'Login failed. Please try again.';
this.loading = false;
}
});
diff --git a/client/src/app/screens/screen-profile/screen-profile.ts b/client/src/app/screens/screen-profile/screen-profile.ts
index f4db7e9..f16a718 100644
--- a/client/src/app/screens/screen-profile/screen-profile.ts
+++ b/client/src/app/screens/screen-profile/screen-profile.ts
@@ -36,7 +36,7 @@ export class ScreenProfile implements OnInit{
logOut(){
this.api.logout().subscribe({
next: () => {
- this.router.navigate(['login'])
+ this.router.navigate(['/'])
},
error: (err) => {
console.error('Error logging out:', err)
diff --git a/client/src/app/services/api.ts b/client/src/app/services/api.ts
index eb80c66..b4ea88b 100644
--- a/client/src/app/services/api.ts
+++ b/client/src/app/services/api.ts
@@ -24,15 +24,22 @@ export class APIService {
);
}
logout(): Observable{
- return this.http.post(`${this.apiUrl}/auth/logout`, {});
+ return this.http.post(`${this.apiUrl}/auth/logout`, {}).pipe(
+ tap({
+ next: () => this.isAuthenticatedSubject.next(false),
+ error: () => this.isAuthenticatedSubject.next(false),
+ })
+ );
+ }
+ clearAuthState() {
+ this.isAuthenticatedSubject.next(false);
}
checkAuthStatus(): Observable {
return this.http.get(`${this.apiUrl}/auth/status`).pipe(
map(() => true),
catchError(() => of(false)),
- tap({
- next: () => this.isAuthenticatedSubject.next(true),
- error: () => this.isAuthenticatedSubject.next(false),
+ tap( authenticated => {
+ this.isAuthenticatedSubject.next(authenticated);
})
);
}
diff --git a/client/src/app/services/auth-interceptor.spec.ts b/client/src/app/services/auth-interceptor.spec.ts
new file mode 100644
index 0000000..5e54858
--- /dev/null
+++ b/client/src/app/services/auth-interceptor.spec.ts
@@ -0,0 +1,17 @@
+import { TestBed } from '@angular/core/testing';
+import { HttpInterceptorFn } from '@angular/common/http';
+
+import { authInterceptor } from './auth-interceptor';
+
+describe('authInterceptor', () => {
+ const interceptor: HttpInterceptorFn = (req, next) =>
+ TestBed.runInInjectionContext(() => authInterceptor(req, next));
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ });
+
+ it('should be created', () => {
+ expect(interceptor).toBeTruthy();
+ });
+});
diff --git a/client/src/app/services/auth-interceptor.ts b/client/src/app/services/auth-interceptor.ts
new file mode 100644
index 0000000..f90d84c
--- /dev/null
+++ b/client/src/app/services/auth-interceptor.ts
@@ -0,0 +1,19 @@
+import { HttpErrorResponse, HttpEventType, HttpInterceptorFn } from '@angular/common/http';
+import { catchError, throwError } from 'rxjs';
+import { APIService } from './api';
+import { inject } from '@angular/core';
+import { Router } from '@angular/router';
+
+export const authInterceptor: HttpInterceptorFn = (req, next) => {
+ const api = inject(APIService);
+ const router = inject(Router)
+ return next(req).pipe(
+ catchError((err: HttpErrorResponse) => {
+ if (err.status === 401){
+ api.clearAuthState();
+ router.createUrlTree(['/login']);
+ }
+ return throwError(() => err);
+ })
+ )
+};