import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { environment } from 'src/environments/environment'; import { sha256 } from 'sha.js'; import { Router } from '@angular/router'; class Answer { success: Boolean = false } class PresaltAnswer extends Answer { presalt: string = '' } class AuthenticateAnswer extends Answer { token: string = '' } class CheckTokenAnswer extends Answer { valid: Boolean = false } class TokenData { iss: string = '' dat: string = '' typ: string = '' usr: string = '' } @Injectable({ providedIn: 'root' }) export class AccountService { public username = '?'; private userId: bigint = BigInt(0); private userToken: bigint = BigInt(0); constructor(private http: HttpClient, private router: Router) { this.updateUserInfo() } public login(username: string, password: string, onFail: Function, onConnectionError: Function) { this.http.post(`${environment.apiURL}/users/presalt`, {username}) .subscribe((answer: Object) => { var presaltAnswer = Object.assign(new PresaltAnswer(), answer) if (!presaltAnswer.success) { onFail() return } // this presalting and hashing is done to protect the user's password in case of a man-in-the-middle-attack, // where an attacker might be able to intercept a password if not hashed before. The password still has to be // hashed on the server-side to be sure but this method prevents dumb users from having their passweord // they use everywhere easily leaked, because you still need to crack this one layer first // also, typical automated hacking software will hopefully be confused by my weird code var passwordHash = new sha256().update(password + presaltAnswer.presalt).digest('hex') this.http.post(`${environment.apiURL}/users/authenticate`, {username, passwordHash}) .subscribe((answer) => { var authenticateAnswer = Object.assign(new AuthenticateAnswer(), answer) var loginSuccess = authenticateAnswer.success == true if (loginSuccess) { localStorage.setItem('userToken', authenticateAnswer.token) this.updateUserInfo() this.username = username this.router.navigate(['admin']) } else { onFail() } }, (error) => {onConnectionError()}) }, (error) => {onConnectionError()}) } updateUserInfo() { var token = localStorage.getItem('userToken') if (token == null) { this.username = '' return } this.http.post(`${environment.apiURL}/users/checkToken`, {token}).subscribe((answer: Object) => { var checkTokenAnswer = Object.assign(new CheckTokenAnswer(), answer) if (checkTokenAnswer.valid == true) { var data = JSON.parse(atob(token?.split('.')[1] as string)) var tokenData = Object.assign(new TokenData(), data) this.username = tokenData.usr } else { this.username = '' } }, () => { this.username = '' }) } logout() { this.username = '' localStorage.removeItem('userToken') } }