diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts
index 2ffb389..763c832 100644
--- a/frontend/src/app/app.component.ts
+++ b/frontend/src/app/app.component.ts
@@ -1,15 +1,33 @@
-import { Component, inject } from '@angular/core';
-import { RouterOutlet } from '@angular/router';
+import { Component, OnInit } from '@angular/core';
+import { RouterOutlet, Router } from '@angular/router';
import { AuthService } from './shared/service';
@Component({
selector: 'app-root',
standalone: true,
- providers: [AuthService],
+ providers: [],
imports: [RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.scss',
})
-export class AppComponent {
- private readonly authService: AuthService = inject(AuthService);
+export class AppComponent implements OnInit {
+ public constructor(
+ private readonly authService: AuthService,
+ private readonly router: Router
+ ) {}
+
+ public ngOnInit(): void {
+ this.checkAuthentication();
+ }
+
+ private checkAuthentication(): void {
+ this.authService.isAuthenticated$.subscribe((isAuthenticated: boolean) => {
+ if (isAuthenticated) {
+ console.log('User is authenticated');
+ this.router.navigateByUrl('dashboard');
+ } else {
+ this.router.navigateByUrl('signup');
+ }
+ });
+ }
}
diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts
index 5488cff..2608632 100644
--- a/frontend/src/app/app.routes.ts
+++ b/frontend/src/app/app.routes.ts
@@ -1,7 +1,12 @@
import { Routes } from '@angular/router';
-export const routes: Routes = [
- { path: '', pathMatch: 'full', redirectTo: '' },
+import { AuthGuard } from './shared/guard/auth.guard';
+
+const publicRoutes: Routes = [
+ {
+ path: '',
+ loadComponent: () => import('./app.component').then((m) => m.AppComponent),
+ },
{
path: 'signup',
loadComponent: () =>
@@ -17,3 +22,25 @@ export const routes: Routes = [
),
},
];
+
+const protectedRoutes: Routes = [
+ {
+ path: 'dashboard',
+ loadComponent: () =>
+ import('./pages/dashboard-root/dashboard-root.component').then(
+ (m) => m.DashboardRootComponent
+ ),
+ canActivate: [AuthGuard],
+ },
+];
+
+export const routes: Routes = [
+ {
+ path: '',
+ children: [
+ ...publicRoutes,
+ ...protectedRoutes,
+ { path: '', redirectTo: '', pathMatch: 'full' },
+ ],
+ },
+];
diff --git a/frontend/src/app/pages/dashboard-root/dashboard-root.component.html b/frontend/src/app/pages/dashboard-root/dashboard-root.component.html
new file mode 100644
index 0000000..f3e333e
--- /dev/null
+++ b/frontend/src/app/pages/dashboard-root/dashboard-root.component.html
@@ -0,0 +1 @@
+
Hello World
diff --git a/frontend/src/app/pages/dashboard-root/dashboard-root.component.scss b/frontend/src/app/pages/dashboard-root/dashboard-root.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/app/pages/dashboard-root/dashboard-root.component.ts b/frontend/src/app/pages/dashboard-root/dashboard-root.component.ts
new file mode 100644
index 0000000..6a160af
--- /dev/null
+++ b/frontend/src/app/pages/dashboard-root/dashboard-root.component.ts
@@ -0,0 +1,14 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+
+@Component({
+ selector: 'app-dashboard-root',
+ standalone: true,
+ imports: [],
+ providers: [],
+ templateUrl: './dashboard-root.component.html',
+ styleUrl: './dashboard-root.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class DashboardRootComponent {
+ public constructor() {}
+}
diff --git a/frontend/src/app/pages/register-root/register-root.component.ts b/frontend/src/app/pages/register-root/register-root.component.ts
index ccf8788..f70ee29 100644
--- a/frontend/src/app/pages/register-root/register-root.component.ts
+++ b/frontend/src/app/pages/register-root/register-root.component.ts
@@ -47,7 +47,7 @@ type AuthAction = 'register' | 'signup';
PasswordModule,
HttpClientModule,
],
- providers: [AuthService],
+ providers: [],
templateUrl: './register-root.component.html',
styleUrl: './register-root.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
diff --git a/frontend/src/app/shared/guard/auth.guard.ts b/frontend/src/app/shared/guard/auth.guard.ts
new file mode 100644
index 0000000..36fb5f3
--- /dev/null
+++ b/frontend/src/app/shared/guard/auth.guard.ts
@@ -0,0 +1,32 @@
+import { inject } from '@angular/core';
+import {
+ ActivatedRouteSnapshot,
+ CanActivateFn,
+ Router,
+ RouterStateSnapshot,
+ UrlTree,
+} from '@angular/router';
+
+import { Observable } from 'rxjs';
+
+import { AuthService } from '../service';
+
+export const AuthGuard: CanActivateFn = (
+ route: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot
+):
+ | Observable
+ | Promise
+ | boolean
+ | UrlTree => {
+ const authService: AuthService = inject(AuthService);
+ const router: Router = inject(Router);
+
+ authService.isAuthenticated$.subscribe((isAuthenticated: boolean) => {
+ if (!isAuthenticated) {
+ router.navigateByUrl('signup');
+ }
+ });
+
+ return true;
+};
diff --git a/frontend/src/app/shared/interceptors/auth.interceptor.ts b/frontend/src/app/shared/interceptors/auth.interceptor.ts
index 7259b6c..00d0384 100644
--- a/frontend/src/app/shared/interceptors/auth.interceptor.ts
+++ b/frontend/src/app/shared/interceptors/auth.interceptor.ts
@@ -1,51 +1,79 @@
import {
- HttpErrorResponse,
- HttpEvent,
- HttpHandlerFn,
HttpInterceptorFn,
HttpRequest,
+ HttpHandlerFn,
+ HttpEvent,
+ HttpErrorResponse,
} from '@angular/common/http';
import { inject } from '@angular/core';
+import { Router } from '@angular/router';
-import { Observable, catchError, switchMap, throwError } from 'rxjs';
+import { Observable, throwError } from 'rxjs';
+import { catchError, switchMap } from 'rxjs/operators';
import { AuthService } from '../service';
-import { Tokens } from '../types';
export const AuthInterceptor: HttpInterceptorFn = (
request: HttpRequest,
next: HttpHandlerFn
): Observable> => {
- const authService: AuthService = inject(AuthService);
- const accessToken: string | null = authService.access_token;
+ const router = inject(Router);
+ const authService = inject(AuthService);
- if (accessToken) {
- request = request.clone({
+ const handleRequest = (
+ req: HttpRequest
+ ): Observable> => {
+ const accessToken = authService.access_token;
+
+ if (accessToken) {
+ req = addAuthHeader(req, accessToken);
+ }
+ return next(req);
+ };
+
+ const addAuthHeader = (
+ req: HttpRequest,
+ token: string
+ ): HttpRequest => {
+ return req.clone({
setHeaders: {
- Authorization: `Bearer ${accessToken}`,
+ Authorization: `Bearer ${token}`,
},
});
- }
- return next(request).pipe(
- catchError((error: HttpErrorResponse) => {
- if (error.status === 401) {
- return authService.refreshToken().pipe(
- switchMap((tokens: Tokens) => {
- request = request.clone({
- setHeaders: {
- Authorization: `Bearer ${tokens.access_token}`,
- },
- });
- return next(request);
- }),
- catchError((refreshError) => {
- authService.signout();
- return throwError(() => new Error(refreshError));
- })
- );
- }
+ };
- return throwError(() => new Error());
- })
+ const handle401Error = (
+ req: HttpRequest
+ ): Observable> => {
+ console.log(authService.refresh_token);
+ if (!authService.refresh_token) {
+ router.navigateByUrl('signup');
+ return throwError(() => new Error('Authentication required'));
+ }
+
+ return authService.refreshToken().pipe(
+ switchMap((tokens) => {
+ req = addAuthHeader(req, tokens.access_token);
+ return next(req);
+ }),
+ catchError((refreshError) => {
+ router.navigateByUrl('signup');
+ return throwError(() => new Error(refreshError));
+ })
+ );
+ };
+
+ const handleError = (
+ error: HttpErrorResponse,
+ req: HttpRequest
+ ): Observable> => {
+ if (error.status === 401) {
+ return handle401Error(req);
+ }
+ return throwError(() => new Error('Unhandled error'));
+ };
+
+ return handleRequest(request).pipe(
+ catchError((error) => handleError(error, request))
);
};
diff --git a/frontend/src/app/shared/service/auth.service.ts b/frontend/src/app/shared/service/auth.service.ts
index c7a6ea3..381fed3 100644
--- a/frontend/src/app/shared/service/auth.service.ts
+++ b/frontend/src/app/shared/service/auth.service.ts
@@ -1,4 +1,3 @@
-import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, tap } from 'rxjs';
@@ -13,22 +12,28 @@ import { SessionStorageService } from './session-storage.service';
providedIn: 'root',
})
export class AuthService {
+ public isAuthenticated$: BehaviorSubject =
+ new BehaviorSubject(false);
private _access_token: string | null = null;
private _refresh_token: string | null = null;
- private _isAuthenticated$: BehaviorSubject =
- new BehaviorSubject(false);
public get access_token(): string | null {
return this._access_token;
}
+ public get refresh_token(): string | null {
+ return this._refresh_token;
+ }
+
public constructor(
- private readonly httpClient: HttpClient,
private readonly localStorageService: LocalStorageService,
private readonly sessionStorageService: SessionStorageService,
private readonly authenticationApiService: AuthenticationApiService
) {
- //this.autoLogin();
+ this._access_token =
+ this.localStorageService.getItem('access_token');
+ this._refresh_token =
+ this.sessionStorageService.getItem('refresh_token');
}
public signin(credentials: LoginCredentials): void {
@@ -66,31 +71,16 @@ export class AuthService {
this._refresh_token = null;
this.localStorageService.removeItem('access_token');
this.sessionStorageService.removeItem('refresh_token');
- this._isAuthenticated$.next(false);
+ this.isAuthenticated$.next(false);
}
});
}
- public autoLogin(): void {
- const storedAccessToken: string | null =
- this.localStorageService.getItem('access_token');
- const storedRefreshToken: string | null =
- this.sessionStorageService.getItem('refresh_token');
-
- if (storedAccessToken && storedRefreshToken) {
- this._refresh_token = storedRefreshToken;
- this._isAuthenticated$.next(true);
- //TODO Validate tokens with backend or decode JWT to check expiration
- } else {
- this.signout();
- }
- }
-
private handleSuccess(tokens: Tokens): void {
this._access_token = tokens.access_token;
this._refresh_token = tokens.refresh_token;
this.localStorageService.setItem('access_token', tokens.access_token);
this.sessionStorageService.setItem('refresh_token', tokens.refresh_token);
- this._isAuthenticated$.next(true);
+ this.isAuthenticated$.next(true);
}
}