diff --git a/src/app/_services/auth.service.ts b/src/app/_services/auth.service.ts index 8ebc9d8..937f2ce 100644 --- a/src/app/_services/auth.service.ts +++ b/src/app/_services/auth.service.ts @@ -14,7 +14,6 @@ import { BehaviorSubject, Observable } from 'rxjs'; providedIn: 'root', }) export class AuthService { - sessionToken: any; mutableKeyStore: MutableKeyStore; trustedUsers: Array = []; private trustedUsersList: BehaviorSubject> = new BehaviorSubject>( @@ -32,41 +31,46 @@ export class AuthService { async init(): Promise { await this.mutableKeyStore.loadKeyring(); - // TODO setting these together should be atomic - if (sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN'))) { - this.sessionToken = sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN')); - } if (localStorage.getItem(btoa('CICADA_PRIVATE_KEY'))) { await this.mutableKeyStore.importPrivateKey(localStorage.getItem(btoa('CICADA_PRIVATE_KEY'))); } } + + getSessionToken(): string { + return sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN')); + } + + setSessionToken(token): void { + sessionStorage.setItem(btoa('CICADA_SESSION_TOKEN'), token); + } setState(s): void { document.getElementById('state').innerHTML = s; } getWithToken(): Promise { - return new Promise((resolve, reject) => { const headers = { - Authorization: 'Bearer ' + this.sessionToken, + Authorization: 'Bearer ' + this.getSessionToken, 'Content-Type': 'application/json;charset=utf-8', 'x-cic-automerge': 'none', }; const options = { headers, }; - fetch(environment.cicMetaUrl, options).then((response) => { - if (response.status === 401) { - return reject(rejectBody(response)); + return fetch(environment.cicMetaUrl, options).then((response) => { + if (!response.ok) { + this.loggingService.sendErrorLevelMessage('failed to get with auth token.', + this, + { error: "" }); + + return false; } - return resolve(true); + return true; }); - }); } // TODO rename to send signed challenge and set session. Also separate these responsibilities - sendResponse(hobaResponseEncoded: any): Promise { - return new Promise((resolve, reject) => { + sendSignedChallenge(hobaResponseEncoded: any): Promise { const headers = { Authorization: 'HOBA ' + hobaResponseEncoded, 'Content-Type': 'application/json;charset=utf-8', @@ -75,85 +79,55 @@ export class AuthService { const options = { headers, }; - fetch(environment.cicMetaUrl, options).then((response) => { - if (response.status === 401) { - return reject(rejectBody(response)); - } - this.sessionToken = response.headers.get('Token'); - sessionStorage.setItem(btoa('CICADA_SESSION_TOKEN'), this.sessionToken); - this.setState('Click button to log in'); - return resolve(true); - }); - }); + return fetch(environment.cicMetaUrl, options) } getChallenge(): Promise { - return new Promise((resolve, reject) => { - fetch(environment.cicMetaUrl).then(async (response) => { - if (response.status === 401) { - const authHeader: string = response.headers.get('WWW-Authenticate'); - return resolve(hobaParseChallengeHeader(authHeader)); - } - if (!response.ok) { - return reject(rejectBody(response)); - } - }); - }); + return fetch(environment.cicMetaUrl) + .then(response => { + if (response.status === 401) { + const authHeader: string = response.headers.get('WWW-Authenticate'); + return hobaParseChallengeHeader(authHeader); + } + }); } async login(): Promise { - if (this.sessionToken !== undefined) { - try { - const response: boolean = await this.getWithToken(); - return response === true; - } catch (e) { - this.loggingService.sendErrorLevelMessage('Login token failed', this, { error: e }); - } + if (this.getSessionToken()) { + sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN')); } else { - try { const o = await this.getChallenge(); - const response: boolean = await this.loginResponse(o); - return response === true; - } catch (e) { - this.loggingService.sendErrorLevelMessage('Login challenge failed', this, { error: e }); - } - } - return false; - } - async loginResponse(o: { challenge: string; realm: any }): Promise { - return new Promise(async (resolve, reject) => { - try { const r = await signChallenge( - o.challenge, - o.realm, - environment.cicMetaUrl, - this.mutableKeyStore + o.challenge, + o.realm, + environment.cicMetaUrl, + this.mutableKeyStore ); - const response: boolean = await this.sendResponse(r); - resolve(response); - } catch (error) { - if (error instanceof HttpError) { - if (error.status === 403) { - this.errorDialogService.openDialog({ - message: 'You are not authorized to use this system', - }); - } else if (error.status === 401) { - this.errorDialogService.openDialog({ - message: - 'Unable to authenticate with the service. ' + - 'Please speak with the staff at Grassroots ' + - 'Economics for requesting access ' + - 'staff@grassrootseconomics.net.', - }); - } - } else { - // TODO define this error - this.errorDialogService.openDialog({ message: 'Incorrect key passphrase.' }); + + const tokenResponse = await this.sendSignedChallenge(r) + .then(response => { + const token = response.headers.get('Token') + if (token) { + return token + } + if (response.status === 401) { + let e = new HttpError("You are not authorized to use this system", response.status) + throw e + } + if (!response.ok) { + let e = new HttpError("Unknown error from authentication server", response.status) + throw e + } + }) + + if (tokenResponse) { + this.setSessionToken(tokenResponse); + this.setState('Click button to log in'); + return true } - resolve(false); - } - }); + return false + } } loginView(): void { @@ -193,7 +167,6 @@ export class AuthService { logout(): void { sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN')); localStorage.removeItem(btoa('CICADA_PRIVATE_KEY')); - this.sessionToken = undefined; window.location.reload(); } diff --git a/src/app/auth/auth.component.ts b/src/app/auth/auth.component.ts index f9cab02..328997f 100644 --- a/src/app/auth/auth.component.ts +++ b/src/app/auth/auth.component.ts @@ -2,6 +2,8 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { CustomErrorStateMatcher } from '@app/_helpers'; import { AuthService } from '@app/_services'; +import { ErrorDialogService } from '@app/_services/error-dialog.service'; +import { LoggingService } from '@app/_services/logging.service'; import { Router } from '@angular/router'; @Component({ @@ -19,18 +21,14 @@ export class AuthComponent implements OnInit { constructor( private authService: AuthService, private formBuilder: FormBuilder, - private router: Router + private router: Router, + private errorDialogService: ErrorDialogService, ) {} async ngOnInit(): Promise { this.keyForm = this.formBuilder.group({ key: ['', Validators.required], }); - await this.authService.init(); - // if (this.authService.privateKey !== undefined) { - // const setKey = await this.authService.setKey(this.authService.privateKey); - // } - // } } get keyFormStub(): any { @@ -49,19 +47,20 @@ export class AuthComponent implements OnInit { this.loading = false; } - login(): void { - // TODO check if we have privatekey - // Send us to home if we have a private key - // talk to meta somehow - // in the error interceptor if 401/403 handle it - // if 200 go /home - if (this.authService.getPrivateKey()) { - this.router.navigate(['/home']); + async login(): Promise { + try { + const loginResult = await this.authService.login() + if (loginResult) { + this.router.navigate(['/home']); + } + } catch (HttpError) { + this.errorDialogService.openDialog({ + message: HttpError.message, + }); } } switchWindows(): void { - this.authService.sessionToken = undefined; const divOne: HTMLElement = document.getElementById('one'); const divTwo: HTMLElement = document.getElementById('two'); this.toggleDisplay(divOne);