diff --git a/src/app/_services/auth.service.ts b/src/app/_services/auth.service.ts index d29bc43..7256c5d 100644 --- a/src/app/_services/auth.service.ts +++ b/src/app/_services/auth.service.ts @@ -12,7 +12,7 @@ import { HttpError, rejectBody } from '@app/_helpers/global-error-handler'; providedIn: 'root', }) export class AuthService { - sessionToken: any; + //sessionToken: any; mutableKeyStore: MutableKeyStore; constructor( @@ -26,40 +26,47 @@ 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 (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 { + console.log('Setting sessiong token! ', token) + 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) { + console.log("failed to getWithToken...maybe try clearing the token and try again?") + 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', @@ -68,85 +75,105 @@ 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')); + //try { + // // TODO do we need to do this? is it just a test of the token? + // const response: boolean = await this.getWithToken(); + // return response + //} catch (e) { + // this.loggingService.sendErrorLevelMessage('Login token failed', this, { error: e }); + //} } else { try { const o = await this.getChallenge(); - const response: boolean = await this.loginResponse(o); - return response === true; + + const r = await signChallenge( + o.challenge, + o.realm, + environment.cicMetaUrl, + this.mutableKeyStore + ); + + const tokenResponse = await this.sendSignedChallenge(r) + .then(response => { + const token = response.headers.get('Token') + if (token) { + return token + } + if (response.status === 401) { + this.errorDialogService.openDialog({ + message: 'You are not authorized to use this system', + }); + return + } + if (response.status === 403) { + console.log('Getting back a 403 but I think the server should send 200') + //this.errorDialogService.openDialog({ + // message: 'You are not authorized to use this system', + //}); + // return + } + if (!response.ok) { + console.log("Failed to get a login token with signed challenge 😭", response.statusText) + return + } + }) + + if (tokenResponse) { + this.setSessionToken(tokenResponse); + this.setState('Click button to log in'); + return true + } + return false } 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 - ); - 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.' }); - } - resolve(false); - } - }); + const r = await signChallenge( + o.challenge, + o.realm, + environment.cicMetaUrl, + this.mutableKeyStore + ); + + return this.sendSignedChallenge(r); + // 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.' }); + // } + // resolve(false); } loginView(): void { @@ -186,7 +213,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/_services/user.service.ts b/src/app/_services/user.service.ts index 2ee24ca..721b120 100644 --- a/src/app/_services/user.service.ts +++ b/src/app/_services/user.service.ts @@ -42,10 +42,10 @@ export class UserService { private registryService: RegistryService, private authService: AuthService ) { - this.authService.init().then(() => { - this.keystore = authService.mutableKeyStore; - this.signer = new PGPSigner(this.keystore); - }); + //this.authService.init().then(() => { + // this.keystore = authService.mutableKeyStore; + // this.signer = new PGPSigner(this.keystore); + //}); this.registry = registryService.getRegistry(); this.registry.load(); } diff --git a/src/app/auth/auth.component.ts b/src/app/auth/auth.component.ts index f9cab02..7c33f4c 100644 --- a/src/app/auth/auth.component.ts +++ b/src/app/auth/auth.component.ts @@ -26,7 +26,7 @@ export class AuthComponent implements OnInit { this.keyForm = this.formBuilder.group({ key: ['', Validators.required], }); - await this.authService.init(); + //await this.authService.init(); // if (this.authService.privateKey !== undefined) { // const setKey = await this.authService.setKey(this.authService.privateKey); // } @@ -49,19 +49,19 @@ export class AuthComponent implements OnInit { this.loading = false; } - login(): void { + async login(): Promise { // 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()) { + const loginResult = await this.authService.login() + if (loginResult) { this.router.navigate(['/home']); } } switchWindows(): void { - this.authService.sessionToken = undefined; const divOne: HTMLElement = document.getElementById('one'); const divTwo: HTMLElement = document.getElementById('two'); this.toggleDisplay(divOne); diff --git a/src/app/pages/accounts/accounts.component.ts b/src/app/pages/accounts/accounts.component.ts index 853997a..0433ba6 100644 --- a/src/app/pages/accounts/accounts.component.ts +++ b/src/app/pages/accounts/accounts.component.ts @@ -33,21 +33,18 @@ export class AccountsComponent implements OnInit { private loggingService: LoggingService, private router: Router ) { - (async () => { - try { - // TODO it feels like this should be in the onInit handler + } + + async ngOnInit(): Promise { + try { // TODO it feels like this should be in the onInit handler await this.userService.loadAccounts(100); - } catch (error) { + } catch (error) { this.loggingService.sendErrorLevelMessage('Failed to load accounts', this, { error }); - } - })(); + } this.userService .getAccountTypes() .pipe(first()) .subscribe((res) => (this.accountTypes = res)); - } - - ngOnInit(): void { this.userService.accountsSubject.subscribe((accounts) => { this.dataSource = new MatTableDataSource(accounts); this.dataSource.paginator = this.paginator;