From a1606311613f4ad4f7cf43c73a99ee06bdda74e0 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Mon, 7 Dec 2020 17:15:41 +0100 Subject: [PATCH] fix(auth): add better signin signup logic feat(auth): add logout --- src/app/header/header.component.html | 7 +-- src/app/header/header.component.ts | 28 ++++++++++-- src/app/models/user-auth.model.ts | 5 +++ src/app/services/auth.service.ts | 43 +++++++++++++++---- .../signin-modal/signin-modal.component.html | 3 ++ .../signup-modal/signup-modal.component.html | 13 +++--- .../signup-modal/signup-modal.component.ts | 28 +++++++++--- 7 files changed, 100 insertions(+), 27 deletions(-) create mode 100644 src/app/models/user-auth.model.ts diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index 31bc0346c..f933d061e 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -14,14 +14,15 @@ <a routerLink="/projects" [routerLinkActive]="'active'" i18n>Projets</a> --> <a routerLink="/about" [routerLinkActive]="'active'" i18n>Qui sommes-nous ?</a> <!-- <a routerLink="/login" [routerLinkActive]="'active'" i18n><span class="clickable ico-mglass purple"></span></a> --> - <button (click)="isSignUpOpen = !isSignUpOpen"> + <button *ngIf="!isLoggedIn" (click)="isPopUpOpen = !isPopUpOpen"> <span class="ico-profile" fxLayout="column" fxLayoutAlign="center center"> <span class="head"></span> <span class="body"></span> </span> </button> + <button *ngIf="isLoggedIn" (click)="logout()">Logout</button> </div> </div> <app-menu-phone *ngIf="showMenu" (closeEvent)="closeMenu($event)"></app-menu-phone> -<!-- <app-signup-modal [openned]="isSignUpOpen" (closed)="closeSignUpModal()"></app-signup-modal> --> -<app-signin-modal [openned]="isSignUpOpen" (closed)="closeSignUpModal()"></app-signin-modal> +<app-signup-modal *ngIf="displaySignUp" [openned]="isPopUpOpen" (closed)="closeSignUpModal($event)"></app-signup-modal> +<app-signin-modal *ngIf="!displaySignUp" [openned]="isPopUpOpen" (closed)="closeSignInModal()"></app-signin-modal> diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index 475373180..5b8d6e7c7 100644 --- a/src/app/header/header.component.ts +++ b/src/app/header/header.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { AuthService } from '../services/auth.service'; @Component({ selector: 'app-header', @@ -7,9 +8,10 @@ import { Component, OnInit } from '@angular/core'; }) export class HeaderComponent implements OnInit { public showMenu = false; - public isSignUpOpen = false; + public isPopUpOpen = false; + public displaySignUp = true; - constructor() {} + constructor(private authService: AuthService) {} ngOnInit(): void {} public openMenu(): void { @@ -18,7 +20,25 @@ export class HeaderComponent implements OnInit { public closeMenu(): void { this.showMenu = false; } - public closeSignUpModal(): void { - this.isSignUpOpen = false; + + public get isLoggedIn(): boolean { + return this.authService.isLoggedIn(); + } + + public logout(): void { + return this.authService.logout(); + } + + public closeSignInModal(): void { + this.isPopUpOpen = false; + this.displaySignUp = true; + } + + public closeSignUpModal(value: boolean): void { + if (!value) { + this.displaySignUp = false; + } else { + this.isPopUpOpen = false; + } } } diff --git a/src/app/models/user-auth.model.ts b/src/app/models/user-auth.model.ts new file mode 100644 index 000000000..4f3a55e5b --- /dev/null +++ b/src/app/models/user-auth.model.ts @@ -0,0 +1,5 @@ +export class UserAuth { + username: string; + access_token: string; + expires_at: string; +} diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index 0a4e3810b..113cb04be 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -1,24 +1,53 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { DateTime } from 'luxon'; import { BehaviorSubject, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { UserAuth } from '../models/user-auth.model'; import { User } from '../models/user.model'; - @Injectable({ providedIn: 'root', }) export class AuthService { - private userSubject: BehaviorSubject<User>; - public user: Observable<User>; + private userSubject: BehaviorSubject<UserAuth>; + public user: Observable<UserAuth>; constructor(private http: HttpClient) { - this.userSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('user'))); + this.userSubject = new BehaviorSubject<UserAuth>(JSON.parse(localStorage.getItem('user'))); this.user = this.userSubject.asObservable(); } + public get userValue(): UserAuth { + return this.userSubject.value; + } + + public get token(): string { + return this.userSubject.value.access_token; + } + + public logout(): void { + localStorage.removeItem('user'); + this.userSubject.next(null); + } + + public isLoggedIn(): boolean { + if (this.userValue) { + return new DateTime.local().setZone('Europe/Paris') < this.getExpiration(); + } + return false; + } + + public getExpiration(): void { + return DateTime.fromISO(this.userValue.expires_at, { zone: 'Europe/Paris' }); + } + + public register(user: User): Observable<any> { + return this.http.post('api/users', user); + } + public login(email: string, password: string): Observable<any> { return this.http - .post<User>('api/auth/login', { email, password }) + .post<UserAuth>('api/auth/login', { email, password }) .pipe( map((user) => { // store user details and jwt token in local storage to keep user logged in between page refreshes @@ -29,10 +58,6 @@ export class AuthService { ); } - public register(user: User): Observable<any> { - return this.http.post('api/users', user); - } - public verifyUser(userId: string, token: string): Observable<any> { return this.http.post(`api/users/verify/${userId}`, null, { params: { token }, diff --git a/src/app/shared/components/signin-modal/signin-modal.component.html b/src/app/shared/components/signin-modal/signin-modal.component.html index 93f21867f..c4e3795c3 100644 --- a/src/app/shared/components/signin-modal/signin-modal.component.html +++ b/src/app/shared/components/signin-modal/signin-modal.component.html @@ -10,6 +10,7 @@ <label for="email">Email</label> <input type="email" + autocomplete="on" formControlName="email" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.email.errors }" @@ -22,6 +23,7 @@ <label for="password">Mot de passe</label> <input type="password" + autocomplete="on" formControlName="password" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.password.errors }" @@ -38,6 +40,7 @@ <label for="confirmPassword">Confirmation du mot de passe</label> <input type="password" + autocomplete="on" formControlName="confirmPassword" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.confirmPassword.errors }" diff --git a/src/app/shared/components/signup-modal/signup-modal.component.html b/src/app/shared/components/signup-modal/signup-modal.component.html index b9cd6065f..e12cffd1b 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.html +++ b/src/app/shared/components/signup-modal/signup-modal.component.html @@ -7,35 +7,38 @@ <div class="card-body"> <form [formGroup]="loginForm" (ngSubmit)="onSubmit()"> <div class="form-group"> - <label for="username">Username</label> + <label for="username">Identifiant</label> <input type="text" formControlName="username" + autocomplete="on" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.username.errors }" /> <div *ngIf="submitted && f.username.errors" class="invalid-feedback"> - <div *ngIf="f.username.errors.required">Username is required</div> + <div *ngIf="f.username.errors.required">Identifiant requis</div> </div> </div> <div class="form-group"> - <label for="password">Password</label> + <label for="password">Mot de passe</label> <input type="password" formControlName="password" + autocomplete="on" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.password.errors }" /> <div *ngIf="submitted && f.password.errors"> - <div *ngIf="f.password.errors.required">Password is required</div> + <div *ngIf="f.password.errors.required">Mot de passe requis</div> </div> </div> + <div *ngIf="authFailed">Identifiant ou mot de passe invalide</div> <div> <button [disabled]="loading"> <span *ngIf="loading"></span> Login </button> - <a routerLink="../register" class="btn btn-link">Register</a> + <button (click)="sendSwitchToSignIn()">Inscription</button> </div> </form> </div> diff --git a/src/app/shared/components/signup-modal/signup-modal.component.ts b/src/app/shared/components/signup-modal/signup-modal.component.ts index f2704cd5f..1ef87d4d1 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.ts +++ b/src/app/shared/components/signup-modal/signup-modal.component.ts @@ -1,5 +1,6 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { FormGroup, FormBuilder, Validators, AbstractControl } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; import { first } from 'rxjs/operators'; import { AuthService } from '../../../services/auth.service'; @@ -12,17 +13,26 @@ export class SignUpModalComponent implements OnInit { public loginForm: FormGroup; public loading = false; public submitted = false; + public authFailed = false; + public returnUrl: string; - constructor(private formBuilder: FormBuilder, private authService: AuthService) {} + constructor( + private formBuilder: FormBuilder, + private route: ActivatedRoute, + private router: Router, + private authService: AuthService + ) {} @Input() public openned: boolean; - @Output() closed = new EventEmitter(); + @Output() closed = new EventEmitter<boolean>(); ngOnInit(): void { this.loginForm = this.formBuilder.group({ username: ['', Validators.required], password: ['', Validators.required], }); + // get return url from route parameters or default to '/' + this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; } // getter for form fields @@ -31,7 +41,11 @@ export class SignUpModalComponent implements OnInit { } public closeModal(): void { - this.closed.emit(); + this.closed.emit(true); + } + + public sendSwitchToSignIn(): void { + this.closed.emit(false); } public onSubmit(): void { @@ -47,11 +61,13 @@ export class SignUpModalComponent implements OnInit { .login(this.f.username.value, this.f.password.value) .pipe(first()) .subscribe( - (data) => { - //TODO: redirect to ? + () => { + this.router.navigate([this.returnUrl]); + this.closeModal(); }, - (error) => { + () => { this.loading = false; + this.authFailed = true; } ); } -- GitLab