Compare commits
4 Commits
trafic-log
...
882ffe7094
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
882ffe7094 | ||
|
|
136771ab60 | ||
| 3865c26397 | |||
|
|
26a217cd50 |
@@ -1,10 +1,11 @@
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
import { APP_INITIALIZER, ApplicationConfig } from '@angular/core';
|
||||
import { provideRouter, withRouterConfig } from '@angular/router';
|
||||
|
||||
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
|
||||
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||
import { routes } from './app.routes';
|
||||
import { JwtInterceptor } from './core/interceptor/jwt.interceptor';
|
||||
import { AuthenticationService } from './core/service/authentication.service';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
@@ -18,5 +19,11 @@ export const appConfig: ApplicationConfig = {
|
||||
provideAnimationsAsync(),
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: (authenticationService: AuthenticationService) => () => authenticationService.startAuthenticationCheckingProcess(),
|
||||
deps: [AuthenticationService],
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { alreadyAuthenticatedGuard } from './core/guard/already-authenticated.guard';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: 'login',
|
||||
loadComponent: () => import('./pages/login/login.component').then(module => module.LoginComponent)
|
||||
loadComponent: () => import('./pages/login/login.component').then(module => module.LoginComponent),
|
||||
canActivate: [alreadyAuthenticatedGuard]
|
||||
},
|
||||
{
|
||||
path: 'signin',
|
||||
loadComponent: () => import('./pages/signin/signin.component').then(module => module.SigninComponent)
|
||||
loadComponent: () => import('./pages/signin/signin.component').then(module => module.SigninComponent),
|
||||
canActivate: [alreadyAuthenticatedGuard]
|
||||
},
|
||||
{
|
||||
path: 'disconnect',
|
||||
|
||||
18
frontend/src/app/core/guard/already-authenticated.guard.ts
Normal file
18
frontend/src/app/core/guard/already-authenticated.guard.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { inject } from "@angular/core";
|
||||
import { CanActivateFn, Router } from "@angular/router";
|
||||
import { AuthenticationService } from "../service/authentication.service";
|
||||
import { MatSnackBar } from "@angular/material/snack-bar";
|
||||
|
||||
export const alreadyAuthenticatedGuard: CanActivateFn = () => {
|
||||
const authenticationService = inject(AuthenticationService);
|
||||
const router = inject(Router);
|
||||
const snackBar = inject(MatSnackBar);
|
||||
|
||||
if (authenticationService.isAuthenticated()) {
|
||||
router.navigate(['/home']);
|
||||
snackBar.open($localize`You can't access to this page because you are already authenticated.`, $localize`Close`, { duration: 5000 });
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,13 @@ import { CanActivateFn, Router } from "@angular/router";
|
||||
import { AuthenticationService } from "../service/authentication.service";
|
||||
import { MatSnackBar } from "@angular/material/snack-bar";
|
||||
|
||||
export const authenticationGuard: CanActivateFn = () => {
|
||||
export const authenticationGuard: CanActivateFn = async () => {
|
||||
const authenticationService = inject(AuthenticationService);
|
||||
const router = inject(Router);
|
||||
const snackBar = inject(MatSnackBar);
|
||||
|
||||
await authenticationService.checkIsAuthenticated();
|
||||
|
||||
if (authenticationService.isAuthenticated()) {
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -41,23 +41,12 @@ export class JwtInterceptor implements HttpInterceptor {
|
||||
this.isRefreshingToken = true;
|
||||
this.refreshTokenSubject.next(undefined);
|
||||
|
||||
const refreshToken = this.authenticationService.getRefreshToken();
|
||||
if (refreshToken) {
|
||||
const refreshTokenRequest: RefreshTokenRequest = {
|
||||
refreshTokenValue: refreshToken
|
||||
};
|
||||
this.userRestService.refreshToken(refreshTokenRequest)
|
||||
.then(refreshTokenResponse => {
|
||||
this.authenticationService.authenticate(refreshTokenResponse.accessToken, refreshTokenResponse.refreshToken);
|
||||
this.refreshTokenSubject.next(refreshTokenResponse.accessToken);
|
||||
})
|
||||
.catch(() => {
|
||||
return this.handleNoRefreshToken(initialError);
|
||||
})
|
||||
.finally(() => this.isRefreshingToken = false);
|
||||
} else {
|
||||
return this.handleNoRefreshToken(initialError);
|
||||
}
|
||||
this.authenticationService.refreshToken()
|
||||
.then(refreshTokenResponse => {
|
||||
this.refreshTokenSubject.next(refreshTokenResponse.accessToken);
|
||||
})
|
||||
.catch(() => this.handleNoRefreshToken(initialError))
|
||||
.finally(() => this.isRefreshingToken = false);
|
||||
}
|
||||
|
||||
return this.refreshTokenSubject.pipe(
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { inject, Injectable } from "@angular/core";
|
||||
import { BehaviorSubject, interval } from "rxjs";
|
||||
import { User } from "../model/User";
|
||||
import { UserRestService } from "../rest-services/user/user.rest-service";
|
||||
import { RefreshTokenRequest } from "../rest-services/user/model/refresh-token.model";
|
||||
import { LoginResponse } from "../rest-services/user/model/login.model";
|
||||
|
||||
const JWT_PARAM = 'jwt';
|
||||
const REFRESH_TOKEN_PARAM = 'refresh-token';
|
||||
@@ -18,18 +20,30 @@ interface UserDetails {
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthenticationService {
|
||||
private readonly AUTHENTICATION_CHECKING_PERIOD = 5 * 60 * 1000;
|
||||
private isAuthenticatedSubject = new BehaviorSubject<boolean>(false);
|
||||
private readonly userRestService = inject(UserRestService);
|
||||
|
||||
startAuthenticationCheckingProcess(): void {
|
||||
this.checkIsAuthenticated();
|
||||
interval(this.AUTHENTICATION_CHECKING_PERIOD)
|
||||
.subscribe(() => this.checkIsAuthenticated());
|
||||
}
|
||||
|
||||
authenticate(token: string, refreshToken: string): void {
|
||||
localStorage.setItem(JWT_PARAM, token);
|
||||
localStorage.setItem(REFRESH_TOKEN_PARAM, refreshToken);
|
||||
this.isAuthenticatedSubject.next(true);
|
||||
}
|
||||
|
||||
unauthenticate(): void {
|
||||
localStorage.removeItem(JWT_PARAM);
|
||||
localStorage.removeItem(REFRESH_TOKEN_PARAM);
|
||||
this.isAuthenticatedSubject.next(false);
|
||||
}
|
||||
|
||||
isAuthenticated(): boolean {
|
||||
return !!localStorage.getItem(JWT_PARAM);
|
||||
return this.isAuthenticatedSubject.value;
|
||||
}
|
||||
|
||||
getAuthenticatedUser(): User | undefined {
|
||||
@@ -45,7 +59,7 @@ export class AuthenticationService {
|
||||
}
|
||||
|
||||
isTokenExpired(): boolean {
|
||||
let result = false;
|
||||
let result = true;
|
||||
|
||||
const userDetails = this.extractUserDetails();
|
||||
|
||||
@@ -59,8 +73,43 @@ export class AuthenticationService {
|
||||
return result;
|
||||
}
|
||||
|
||||
refreshToken(): Promise<LoginResponse> {
|
||||
const refreshToken = this.getRefreshToken();
|
||||
if (refreshToken) {
|
||||
const refreshTokenRequest: RefreshTokenRequest = {
|
||||
refreshTokenValue: refreshToken
|
||||
};
|
||||
return this.userRestService.refreshToken(refreshTokenRequest)
|
||||
.then(refreshTokenResponse => {
|
||||
this.authenticate(refreshTokenResponse.accessToken, refreshTokenResponse.refreshToken);
|
||||
return refreshTokenResponse;
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.reject('No any refresh token found.');
|
||||
}
|
||||
|
||||
checkIsAuthenticated(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const isTokenExpired = this.isTokenExpired();
|
||||
if (isTokenExpired) {
|
||||
if (this.getRefreshToken()) {
|
||||
this.refreshToken()
|
||||
.then(() => resolve())
|
||||
.catch(() => reject());
|
||||
} else {
|
||||
this.isAuthenticatedSubject.next(false);
|
||||
}
|
||||
} else {
|
||||
this.isAuthenticatedSubject.next(true);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private extractUserFromLocalStorage(): User | undefined {
|
||||
let result: User | undefined = undefined;
|
||||
let result: User | undefined;
|
||||
|
||||
const userDetails = this.extractUserDetails();
|
||||
if (userDetails) {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
@@ -46,6 +46,7 @@
|
||||
"9122763438636464100": "Fermer le menu",
|
||||
"1902100407096396858": "Catégories",
|
||||
"6940115735259407353": "Une erreur est survenue lors du chargement des catégories.",
|
||||
"3516191632292372377": "Vous ne pouvez pas accéder à cette page car vous êtes déjà connecté.",
|
||||
"3450287383703155559": "Vous n'êtes pas connecté. Veuillez vous connecter avant de réessayer.",
|
||||
"5455465794443528807": "You n'êtes pas connecté. Veuillez vous connecter avant de réessayer votre opération.",
|
||||
"4011987306265136481": "Déconnexion...",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"locale": "en-UK",
|
||||
"locale": "en",
|
||||
"translations": {
|
||||
"3603720768157919481": " No ",
|
||||
"4861926948802653243": " Yes ",
|
||||
@@ -46,6 +46,7 @@
|
||||
"9122763438636464100": "Close the menu",
|
||||
"1902100407096396858": "Categories",
|
||||
"6940115735259407353": "An error occured while loading categories.",
|
||||
"3516191632292372377": "You can't access to this page because you are already authenticated.",
|
||||
"3450287383703155559": "You are unauthenticated. Please, log-in first.",
|
||||
"5455465794443528807": "You are unauthenticated. Please, re-authenticate before retrying your action.",
|
||||
"4011987306265136481": "Disconnection...",
|
||||
|
||||
Reference in New Issue
Block a user