diff --git a/package.json b/package.json index b93082c..fa2d08b 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,9 @@ "e2e": "ng e2e" }, "private": true, + "browser": { + "fs": false + }, "dependencies": { "@angular/animations": "~12.2.14", "@angular/cdk": "^12.2.13", @@ -21,10 +24,11 @@ "@angular/platform-browser": "~12.2.14", "@angular/platform-browser-dynamic": "~12.2.14", "@angular/router": "~12.2.14", + "@js-joda/core": "^4.3.1", "@mdi/angular-material": "^6.5.95", "bootstrap": "^4.5.2", "copy-webpack-plugin": "^10.0.0", - "@js-joda/core": "^4.3.1", + "jwt-decode": "^3.1.2", "material-design-icons": "^3.0.1", "ngx-material-file-input": "^2.1.1", "rxjs": "^7.4.0", diff --git a/src/app/modules/accounts/accounts.ts b/src/app/modules/accounts/accounts.ts index 917f130..2c42c84 100644 --- a/src/app/modules/accounts/accounts.ts +++ b/src/app/modules/accounts/accounts.ts @@ -48,18 +48,17 @@ export class Login extends ErrorHandlingComponent { } submit() { + this.subscribeAndNavigate( + this.accountService.login(this.userIdControl.value, this.passwordControl.value), + '/color' + ) // Does not use SubscribingComponent shortcut because backend doesn't return expected error type - this.accountService.login(this.userIdControl.value, this.passwordControl.value) - .pipe(take(1), takeUntil(this.destroy$)) - .subscribe({ - error: error => this.handleLoginError(error) - }) - } - - private handleLoginError(error) { - if (error.status === 403) { - this.alertService.pushError('Les identifiants entrés sont invalides') - } + // this.accountService.login(this.userIdControl.value, this.passwordControl.value) + // .pipe(take(1), takeUntil(this.destroy$)) + // .subscribe({ + // next: () => this.urlUtils.navigateTo('/color'), + // error: error => this.handleLoginError(error) + // }) } get controls(): { userId: FormControl, password: FormControl } { diff --git a/src/app/modules/accounts/services/account.service.ts b/src/app/modules/accounts/services/account.service.ts index 22ad500..7295c3d 100644 --- a/src/app/modules/accounts/services/account.service.ts +++ b/src/app/modules/accounts/services/account.service.ts @@ -1,14 +1,15 @@ import {Injectable, OnDestroy} from '@angular/core' import {Observable, Subject} from 'rxjs' -import {take, takeUntil, tap} from 'rxjs/operators' +import {take, takeUntil} from 'rxjs/operators' import {AppState} from '../../shared/app-state' import {HttpClient, HttpResponse} from '@angular/common/http' import {environment} from '../../../../environments/environment' import {ApiService} from '../../shared/service/api.service' -import {User, Permission} from '../../shared/model/user' +import {Permission} from '../../shared/model/user' import {ErrorService} from '../../shared/service/error.service' import {globalLoadingWheel} from '../../shared/components/loading-wheel/loading-wheel.component' import {AlertService} from '../../shared/service/alert.service' +import {JwtService} from "./jwt.service"; @Injectable({ providedIn: 'root' @@ -20,6 +21,7 @@ export class AccountService implements OnDestroy { private http: HttpClient, private api: ApiService, private appState: AppState, + private jwtService: JwtService, private errorService: ErrorService, private alertService: AlertService ) { @@ -37,47 +39,61 @@ export class AccountService implements OnDestroy { checkAuthenticationStatus() { if (!this.appState.authenticatedUser) { // Try to get current default group user - this.http.get(`${environment.apiUrl}/user/current`, {withCredentials: true}) - .pipe( - take(1), - takeUntil(this.destroy$), - ).subscribe( - { - next: user => this.appState.authenticatedUser = user, - error: err => { - if (err.status === 404 || err.status === 403) { - console.warn('No default user is defined on this computer') - } else { - this.errorService.handleError(err) - } - } - }) + // this.http.get(`${environment.apiUrl}/user/current`, {withCredentials: true}) + // .pipe( + // take(1), + // takeUntil(this.destroy$), + // ).subscribe( + // { + // next: user => this.appState.authenticatedUser = user, + // error: err => { + // if (err.status === 404 || err.status === 403) { + // console.warn('No default user is defined on this computer') + // } else { + // this.errorService.handleError(err) + // } + // } + // }) } } login(userId: number, password: string): Observable { - globalLoadingWheel.show() - const request$ = this.http.post(`${environment.apiUrl}/login`, {id: userId, password}, { + const subject = new Subject() + + this.http.post(`${environment.apiUrl}/login`, {id: userId, password}, { withCredentials: true, observe: 'response' as 'body' }).pipe( take(1), takeUntil(this.destroy$) - ) + ).subscribe({ + next: (response: HttpResponse) => { + this.loginUser(response) - request$.subscribe({ - next: (response: HttpResponse) => { - globalLoadingWheel.hide() - - // TODO: Login user + subject.next() + subject.complete() }, error: error => { - globalLoadingWheel.hide() - this.errorService.handleError(error) + if (error.status === 403) { + this.alertService.pushError('Les identifiants entrés sont invalides') + } else { + this.errorService.handleError(error) + } + + subject.next() + subject.complete() } }) - return request$ + return subject + } + + private loginUser(response: HttpResponse) { + const authorization = response.headers.get("Authorization") + const jwt = this.jwtService.parseJwt(authorization) + + this.appState.authenticatedUser = jwt.user + this.appState.authenticationExpiration = jwt.exp } loginOld(id: number, password: string, success: () => void) { @@ -129,18 +145,19 @@ export class AccountService implements OnDestroy { } private setLoggedInUserFromApi() { - this.api.get('/user/current', true) - .pipe( - take(1), - takeUntil(this.destroy$) - ) - .subscribe({ - next: user => { - this.appState.authenticatedUser = user - // At this point the loading wheel should be visible - globalLoadingWheel.hide() - }, - error: err => this.errorService.handleError(err) - }) + // this.api.get('/user/current', true) + // .pipe( + // take(1), + // takeUntil(this.destroy$) + // ) + // .subscribe({ + // next: user => { + // this.appState.authenticatedUser = user + // At this point the loading wheel should be visible + // globalLoadingWheel.hide() + // }, + // error: err => this.errorService.handleError(err) + // }) + console.warn("REMOVE THIS") } } diff --git a/src/app/modules/accounts/services/jwt.service.ts b/src/app/modules/accounts/services/jwt.service.ts new file mode 100644 index 0000000..d2a88f5 --- /dev/null +++ b/src/app/modules/accounts/services/jwt.service.ts @@ -0,0 +1,25 @@ +import {Injectable} from "@angular/core"; +import {User} from "../../shared/model/user"; +import jwtDecode from "jwt-decode"; +import {parseJson} from "@angular/cli/utilities/json-file"; + +@Injectable({ + providedIn: 'root' +}) +export class JwtService { + parseJwt(jwt: string): CreJwt { + const decoded = jwtDecode(jwt) as any + + return { + sub: decoded.sub, + exp: decoded.exp, + user: parseJson(decoded.user) + } + } +} + +interface CreJwt { + readonly sub: string + readonly exp: number, + readonly user: User +} diff --git a/src/app/modules/shared/app-state.ts b/src/app/modules/shared/app-state.ts index f850b3e..1504d28 100644 --- a/src/app/modules/shared/app-state.ts +++ b/src/app/modules/shared/app-state.ts @@ -2,6 +2,7 @@ import {Injectable} from '@angular/core' import {User} from './model/user' import {Subject} from 'rxjs' import {Title} from '@angular/platform-browser' +import jwtDecode from "jwt-decode"; @Injectable({ providedIn: 'root' @@ -59,8 +60,10 @@ export class AppState { set authenticatedUser(value: User) { if (value === null) { + console.log(1) sessionStorage.removeItem(this.KEY_LOGGED_IN_USER) } else { + console.log(2) sessionStorage.setItem(this.KEY_LOGGED_IN_USER, JSON.stringify(value)) } this.authenticatedUser$.next({