Feature: Added Login / Register Feature #3

Merged
igorpropisnov merged 26 commits from feature/register-view into main 2024-05-21 21:24:11 +02:00
7 changed files with 130 additions and 10 deletions
Showing only changes of commit bdc9ec3ee0 - Show all commits

View File

@ -1,38 +1,77 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { LoginCredentials, Tokens } from '../types'; import { LoginCredentials, Tokens } from '../types';
import { LocalStorageService } from './local-storage.service';
import { SessionStorageService } from './session-storage.service';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class AuthService { export class AuthService {
private isAuthenticated: boolean = false; private readonly path: string = '/api/auth';
private access_token: string | null = null; private access_token: string | null = null;
private refresh_token: string | null = null; private refresh_token: string | null = null;
private isAuthenticated$: BehaviorSubject<boolean> =
new BehaviorSubject<boolean>(false);
public constructor( public constructor(
private readonly httpClient: HttpClient, private readonly httpClient: HttpClient,
private readonly router: Router private readonly localStorageService: LocalStorageService,
private readonly sessionStorageService: SessionStorageService
) {} ) {}
public signin(credentials: LoginCredentials): void { public signin(credentials: LoginCredentials): void {
this.httpClient this.httpClient
.post<Tokens>(environment.api.base + '/api/auth/signin', credentials) .post<Tokens>(environment.api.base + `${this.path}/signin`, credentials)
.subscribe((response: Tokens) => { .subscribe((response: Tokens) => {
this.access_token = response.access_token; this.handleSuccess(response);
this.refresh_token = response.refresh_token;
}); });
} }
public signup(credentials: LoginCredentials): void { public signup(credentials: LoginCredentials): void {
this.httpClient this.httpClient
.post<Tokens>(environment.api.base + '/api/auth/signup', credentials) .post<Tokens>(environment.api.base + `${this.path}/signup`, credentials)
.subscribe((response: Tokens) => { .subscribe((response: Tokens) => {
this.access_token = response.access_token; // The checked accept terms should be saved with a timestamp in the db
this.refresh_token = response.refresh_token; this.handleSuccess(response);
}); });
} }
public signout(): void {
this.access_token = null;
this.refresh_token = null;
this.localStorageService.removeItem('access_token');
this.sessionStorageService.removeItem('refresh_token');
this.isAuthenticated$.next(false);
}
public refreshToken(): void {
const headers = new HttpHeaders().set(
'Authorization',
'Bearer ' + this.refresh_token
);
this.httpClient
.post<Tokens>(
environment.api.base + `${this.path}/refresh`,
{},
{ headers: headers }
)
.subscribe((response: Tokens) => {
this.handleSuccess(response);
});
}
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);
}
} }

View File

@ -0,0 +1,33 @@
import { EncryptionService } from './encryption.service';
import { SanitizationService } from './sanitization.service';
export abstract class BaseStorageService {
protected abstract getStorage(): Storage;
public setItem<T>(key: string, value: T): void {
const sanitizedValue = SanitizationService.sanitize(JSON.stringify(value));
const encryptedValue = EncryptionService.encrypt(sanitizedValue);
this.getStorage().setItem(key, encryptedValue);
}
public getItem<T>(key: string): T | null {
const encryptedValue = this.getStorage().getItem(key);
if (encryptedValue) {
const decryptedValue = EncryptionService.decrypt(encryptedValue);
const sanitizedValue = SanitizationService.sanitize(decryptedValue);
return JSON.parse(sanitizedValue) as T;
}
return null;
}
public removeItem(key: string): void {
this.getStorage().removeItem(key);
}
public clear(): void {
this.getStorage().clear();
}
}

View File

@ -0,0 +1,15 @@
import * as CryptoJS from 'crypto-js';
import { environment } from '../../../environments/environment';
export class EncryptionService {
private static readonly key: string = environment.security.encryptionKey;
public static encrypt(data: string): string {
return CryptoJS.AES.encrypt(data, this.key).toString();
}
public static decrypt(data: string): string {
return CryptoJS.AES.decrypt(data, this.key).toString(CryptoJS.enc.Utf8);
}
}

View File

@ -1 +1,3 @@
export * from './auth.service'; export * from './auth.service';
export * from './local-storage.service';
export * from './session-storage.service';

View File

@ -0,0 +1,12 @@
import { Injectable } from '@angular/core';
import { BaseStorageService } from './base-storage.service';
@Injectable({
providedIn: 'root',
})
export class LocalStorageService extends BaseStorageService {
protected getStorage(): Storage {
return localStorage;
}
}

View File

@ -0,0 +1,7 @@
import DOMPurify from 'dompurify';
export class SanitizationService {
public static sanitize(data: string): string {
return DOMPurify.sanitize(data);
}
}

View File

@ -0,0 +1,12 @@
import { Injectable } from '@angular/core';
import { BaseStorageService } from './base-storage.service';
@Injectable({
providedIn: 'root',
})
export class SessionStorageService extends BaseStorageService {
protected getStorage(): Storage {
return sessionStorage;
}
}