diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts index 8c0d35dd9..8fe16aad4 100644 --- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts +++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts @@ -7,7 +7,7 @@ import { AudioService } from '../../services/audio.service'; import { ETA, EtaService } from '../../services/eta.service'; import { Transaction } from '../../interfaces/electrs.interface'; import { MiningStats } from '../../services/mining.service'; -import { StorageService } from '../../services/storage.service'; +import { IAuth, AuthServiceMempool } from '../../services/auth.service'; export type PaymentMethod = 'balance' | 'bitcoin' | 'cashapp'; @@ -72,7 +72,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy { simpleMode: boolean = true; paymentMethod: 'cashapp' | 'btcpay'; - user: any = undefined; + authSubscription$: Subscription; + auth: IAuth | null = null; // accelerator stuff square: { appId: string, locationId: string}; @@ -109,16 +110,22 @@ export class AccelerateCheckout implements OnInit, OnDestroy { constructor( public stateService: StateService, private servicesApiService: ServicesApiServices, - private storageService: StorageService, private etaService: EtaService, private audioService: AudioService, - private cd: ChangeDetectorRef + private cd: ChangeDetectorRef, + private authService: AuthServiceMempool ) { this.accelerationUUID = window.crypto.randomUUID(); } ngOnInit() { - this.user = this.storageService.getAuth()?.user ?? null; + this.authSubscription$ = this.authService.getAuth$().subscribe((auth) => { + this.auth = auth; + this.estimate = null; + this.moveToStep('summary'); + }); + this.authService.refreshAuth$().subscribe(); + const urlParams = new URLSearchParams(window.location.search); if (urlParams.get('cash_request_id')) { // Redirected from cashapp this.moveToStep('processing'); @@ -140,6 +147,9 @@ export class AccelerateCheckout implements OnInit, OnDestroy { if (this.estimateSubscription) { this.estimateSubscription.unsubscribe(); } + if (this.authSubscription$) { + this.authSubscription$.unsubscribe(); + } } ngOnChanges(changes: SimpleChanges): void { @@ -465,8 +475,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { } isLoggedIn(): boolean { - const auth = this.storageService.getAuth(); - return auth !== null; + return this.auth !== null; } get step() { diff --git a/frontend/src/app/components/menu/menu.component.ts b/frontend/src/app/components/menu/menu.component.ts index fe9fd5f09..719495bb0 100644 --- a/frontend/src/app/components/menu/menu.component.ts +++ b/frontend/src/app/components/menu/menu.component.ts @@ -5,6 +5,7 @@ import { StorageService } from '../../services/storage.service'; import { Router, NavigationStart } from '@angular/router'; import { StateService } from '../../services/state.service'; import { IUser, ServicesApiServices } from '../../services/services-api.service'; +import { AuthServiceMempool } from '../../services/auth.service'; @Component({ selector: 'app-menu', @@ -26,7 +27,8 @@ export class MenuComponent implements OnInit, OnDestroy { private servicesApiServices: ServicesApiServices, private storageService: StorageService, private router: Router, - private stateService: StateService + private stateService: StateService, + private authService: AuthServiceMempool ) {} ngOnInit(): void { @@ -61,6 +63,7 @@ export class MenuComponent implements OnInit, OnDestroy { this.loggedOut.emit(true); if (this.stateService.env.GIT_COMMIT_HASH_MEMPOOL_SPACE) { this.userMenuGroups$ = this.servicesApiServices.getUserMenuGroups$(); + this.authService.logout(); if (window.location.toString().includes('services')) { this.router.navigateByUrl('/login'); } diff --git a/frontend/src/app/services/auth.service.ts b/frontend/src/app/services/auth.service.ts new file mode 100644 index 000000000..7a4fcda24 --- /dev/null +++ b/frontend/src/app/services/auth.service.ts @@ -0,0 +1,71 @@ +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { map, Observable, ReplaySubject, switchMap, tap } from 'rxjs'; +import { ServicesApiServices } from './services-api.service'; + +export interface IAuth { + token: string; + user: { + userId: number; + username: string; + } +} + +@Injectable({ + providedIn: 'root' +}) +export class AuthServiceMempool { + private auth$: ReplaySubject = new ReplaySubject(1); + + constructor( + private servicesApiService: ServicesApiServices, + private router: Router, + ) { + const localStorageAuth = localStorage.getItem('auth'); + if (!localStorageAuth || localStorageAuth.length === 0) { + this.setAuth(null); + } else { + try { + this.setAuth(JSON.parse(localStorageAuth)); + } catch (e) { + console.error(`Unable to parse 'auth' from localStorage`, e); + localStorage.removeItem('auth'); + this.setAuth(null); + } + } + } + + refreshAuth$(): Observable { + return this.servicesApiService.getJWT$() + .pipe( + tap((user) => { + this.setAuth(user); + }), + map((user) => { + return user; + }) + ); + } + + logout() { + this.setAuth(null); + } + + setAuth(auth: any) { + if (!auth) { + localStorage.removeItem('auth'); + } else { + localStorage.setItem('auth', JSON.stringify(auth)); + } + this.auth$.next(auth); + } + + getAuth$(): Observable { + if (!localStorage.getItem('auth')) { + return this.refreshAuth$().pipe( + switchMap(() => this.auth$.asObservable()) + ); + } + return this.auth$.asObservable(); + } +} diff --git a/frontend/src/app/services/services-api.service.ts b/frontend/src/app/services/services-api.service.ts index 546b38730..07e72e0b6 100644 --- a/frontend/src/app/services/services-api.service.ts +++ b/frontend/src/app/services/services-api.service.ts @@ -120,6 +120,10 @@ export class ServicesApiServices { return this.httpClient.post(`${SERVICES_API_PREFIX}/auth/logout`, {}); } + getJWT$() { + return this.httpClient.get(`${SERVICES_API_PREFIX}/auth/getJWT`); + } + getServicesBackendInfo$(): Observable { return this.httpClient.get(`${SERVICES_API_PREFIX}/version`); }