diff --git a/package-lock.json b/package-lock.json index ac97b43..b392308 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@angular/router": "~13.0.0", "bootstrap": "^5.1.3", "bootstrap-icons": "^1.7.2", + "jwt-decode": "^3.1.2", "rxjs": "~7.4.0", "tslib": "^2.3.0", "zone.js": "~0.11.4" @@ -6887,6 +6888,11 @@ "node >= 0.2.0" ] }, + "node_modules/jwt-decode": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" + }, "node_modules/karma": { "version": "6.3.11", "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.11.tgz", @@ -17654,6 +17660,11 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, + "jwt-decode": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" + }, "karma": { "version": "6.3.11", "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.11.tgz", diff --git a/package.json b/package.json index f47f13c..58cdaed 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@angular/router": "~13.0.0", "bootstrap": "^5.1.3", "bootstrap-icons": "^1.7.2", + "jwt-decode": "^3.1.2", "rxjs": "~7.4.0", "tslib": "^2.3.0", "zone.js": "~0.11.4" diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index d6925dd..f4eae8e 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -8,6 +8,7 @@ import { PageNotFoundComponent } from './pages/page-not-found/page-not-found.com import { FiltersPageComponent } from './pages/filters-page/filters-page.component'; import { SigninComponent } from './pages/signin/signin.component'; import { AdminPageComponent } from './pages/admin-page/admin-page.component'; +import { AuthGuard } from './services/auth.guard'; const routes: Routes = [ { path: '', redirectTo: 'home', pathMatch: 'full' }, @@ -16,7 +17,7 @@ const routes: Routes = [ { path: 'favoris', component: FavorisUserComponent }, { path: 'filtres', component: FiltersPageComponent }, { path: 'Deconnexion', redirectTo: 'home'}, - {path: 'restaurants',component: RestoPageComponent}, + {path: 'restaurants', canActivate: [AuthGuard], component: RestoPageComponent}, {path: 'page-not-found',component: PageNotFoundComponent}, {path: 'signin', component: SigninComponent}, {path: 'admin', component: AdminPageComponent}, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 1ad877a..1136115 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -9,7 +9,7 @@ import { CardCategoryComponent } from './card-category/card-category.component'; import { CardRestoComponent } from './card-resto/card-resto.component'; import { FooterComponent } from './footer/footer.component'; import { HomePageComponent } from './pages/home-page/home-page.component'; -import { HttpClientModule } from '@angular/common/http'; +import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { ListCategoriesComponent } from './pages/list-categories/list-categories.component'; import { SigninComponent } from './pages/signin/signin.component'; import { RestoPageComponent } from './pages/resto-page/resto-page.component'; @@ -20,6 +20,7 @@ import { IconComponent } from './filters/icon/icon.component'; import { TemplatePageComponent } from './components/template-page/template-page.component'; import { AdminPageComponent } from './pages/admin-page/admin-page.component'; import { AddRestauComponent } from './admin-component/add-restau/add-restau.component'; +import { AuthInterceptor } from './services/auth.interceptor'; @NgModule({ declarations: [ @@ -48,7 +49,9 @@ import { AddRestauComponent } from './admin-component/add-restau/add-restau.comp FormsModule, ReactiveFormsModule ], - providers: [], + providers: [ + { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } + ], bootstrap: [AppComponent] }) export class AppModule { } diff --git a/src/app/pages/signin/signin.component.ts b/src/app/pages/signin/signin.component.ts index e233553..a7e05e5 100644 --- a/src/app/pages/signin/signin.component.ts +++ b/src/app/pages/signin/signin.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; import { AuthService } from '../../services/auth.service'; @Component({ @@ -8,7 +9,8 @@ import { AuthService } from '../../services/auth.service'; }) export class SigninComponent implements OnInit { public errorForm: boolean; - constructor(private authService: AuthService) { + + constructor(private authService: AuthService , private router: Router) { this.errorForm = false; } @@ -21,7 +23,9 @@ export class SigninComponent implements OnInit { const password = submittedForm.form.value['password']; if(email !== '' && password !== '') { this.authService.signin(email, password).subscribe( - resp => console.log('Component Page Signin: ', resp) + resp => {console.log('Component Page Signin ', resp) + this.router.navigate(['home']) + } ) } else { // afficher une erreur à l'utilisateur diff --git a/src/app/services/auth.guard.spec.ts b/src/app/services/auth.guard.spec.ts new file mode 100644 index 0000000..68889d2 --- /dev/null +++ b/src/app/services/auth.guard.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthGuard } from './auth.guard'; + +describe('AuthGuard', () => { + let guard: AuthGuard; + + beforeEach(() => { + TestBed.configureTestingModule({}); + guard = TestBed.inject(AuthGuard); + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); +}); diff --git a/src/app/services/auth.guard.ts b/src/app/services/auth.guard.ts new file mode 100644 index 0000000..7ae6686 --- /dev/null +++ b/src/app/services/auth.guard.ts @@ -0,0 +1,46 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { Observable } from 'rxjs'; +import { environment } from 'src/environments/environment'; +import jwt_decode from 'jwt-decode'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthGuard implements CanActivate { + private tokenKey: string; + constructor(private router: Router){ + this.tokenKey = environment.tokenKey; + } + + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { + + const token = localStorage.getItem(this.tokenKey); + + if(token) { + const decodedToken = jwt_decode(token); + + console.log('decodedToken : ', decodedToken); + + if(decodedToken.exp) { + console.log('Date d\'exp decodedToken : ', decodedToken.exp); + const dateExp = new Date(decodedToken.exp * 1000); + if(new Date() >= dateExp) { + // le token a expiré, je n'autorise pas l'accès + this.router.navigate(['signin']); + return false; + } + } + + console.log("C'est ok ! ") + return true; + } else { + console.log("You shall not pass !!!!") + this.router.navigate(['signin']); // redirection de notre utilisateur vers une url de notre application (dans notre code TS) + return false; + } + } + +} diff --git a/src/app/services/auth.interceptor.spec.ts b/src/app/services/auth.interceptor.spec.ts new file mode 100644 index 0000000..7ab58db --- /dev/null +++ b/src/app/services/auth.interceptor.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthInterceptor } from './auth.interceptor'; + +describe('AuthInterceptor', () => { + beforeEach(() => TestBed.configureTestingModule({ + providers: [ + AuthInterceptor + ] + })); + + it('should be created', () => { + const interceptor: AuthInterceptor = TestBed.inject(AuthInterceptor); + expect(interceptor).toBeTruthy(); + }); +}); diff --git a/src/app/services/auth.interceptor.ts b/src/app/services/auth.interceptor.ts new file mode 100644 index 0000000..5e4ecf1 --- /dev/null +++ b/src/app/services/auth.interceptor.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { + HttpRequest, + HttpHandler, + HttpEvent, + HttpInterceptor +} from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { TokenService } from './token.service'; + +@Injectable() +export class AuthInterceptor implements HttpInterceptor { + + constructor(private tokenService: TokenService) {} + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + console.log('Coucou je suis le videur ! '); + const token = this.tokenService.getToken(); + if(token) { + + const authReq = request.clone( + { + headers : request.headers.set('Authorization', `Bearer ${token}`) + } + ) + return next.handle(authReq); + } else { + return next.handle(request); + } + } +} diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index c1f66ff..b3d31e6 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -18,18 +18,18 @@ export class AuthService { this.tokenKey = environment.tokenKey; } - signup(newUser: User): Observable { - // const body = { - // firstName: firstName, - // lastName: lastName, - // email: email, - // password: password - // }; + // signup(): Observable { + // // const body = { + // // firstName: firstName, + // // lastName: lastName, + // // email: email, + // // password: password + // // }; - console.log("Mon nouvel utilisateur : ", newUser); + // console.log("Mon nouvel utilisateur : ", newUser); - return this.http.post(`${this.apiUrl}/register`, newUser); - } + // return this.http.post(`${this.apiUrl}/register`, newUser); + // } signin(email: string, password: string): Observable { const body = { @@ -41,27 +41,28 @@ export class AuthService { // Modifier cette partie ci-dessous : // - pour pouvoir stocker dans le localstorage notre accesstoken - // - Sous la clé "TOKEN-LBP" + // - Sous la clé "TOKEN-SIMPLEAT" - return this.http.post(`${this.apiUrl}/login`, body).pipe( + return this.http.post(`${this.apiUrl}/signin`, body).pipe( map((x: any) => { - console.log('Service : ', x.accessToken); + + console.log('Service : ', x.token); // Modification à faire ici - localStorage.setItem(this.tokenKey, x.accessToken); + localStorage.setItem(this.tokenKey, x.token); return x; // permet de renvoyer la réponse à l'initiateur (page Signin) après le traitement du map }) ); } - forgotPassword(email: string, password: string): Observable { - const body = { - email: email, - password: password - }; + // forgotPassword(email: string, password: string): Observable { + // const body = { + // email: email, + // password: password + // }; - console.log("Mon body : ", body); + // console.log("Mon body : ", body); - return this.http.post(`${this.apiUrl}/forgot-psw`, body); - } + // return this.http.post(`${this.apiUrl}/forgot-psw`, body); + // } } diff --git a/src/app/services/token.service.spec.ts b/src/app/services/token.service.spec.ts new file mode 100644 index 0000000..7930902 --- /dev/null +++ b/src/app/services/token.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { TokenService } from './token.service'; + +describe('TokenService', () => { + let service: TokenService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(TokenService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/token.service.ts b/src/app/services/token.service.ts new file mode 100644 index 0000000..8db8aa4 --- /dev/null +++ b/src/app/services/token.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { environment } from 'src/environments/environment'; +import jwt_decode from 'jwt-decode'; + +@Injectable({ + providedIn: 'root' +}) +export class TokenService { + tokenKey = environment.tokenKey; + + constructor() { } + + public getToken(): string | null { + const token = localStorage.getItem(this.tokenKey); + if(token) { + return token; + } else { + return null; + } + } + + public getCurrentUserId(): number | null { + const token = this.getToken(); + if(token) { + const decodedToken = jwt_decode(token); + const userId = decodedToken.sub; + return userId; + } else { + return null; + } + } +}