import { Observable, combineLatest, distinctUntilChanged, firstValueFrom, map, of, shareReplay, switchMap, } from "rxjs"; import { ApiService } from "../../abstractions/api.service"; import { CryptoService } from "../../platform/abstractions/crypto.service"; import { MessagingService } from "../../platform/abstractions/messaging.service"; import { StateService } from "../../platform/abstractions/state.service"; import { Utils } from "../../platform/misc/utils"; import { UserId } from "../../types/guid"; import { AccountService } from "../abstractions/account.service"; import { AuthService as AuthServiceAbstraction } from "../abstractions/auth.service"; import { TokenService } from "../abstractions/token.service"; import { AuthenticationStatus } from "../enums/authentication-status"; export class AuthService implements AuthServiceAbstraction { activeAccountStatus$: Observable; authStatuses$: Observable>; constructor( protected accountService: AccountService, protected messagingService: MessagingService, protected cryptoService: CryptoService, protected apiService: ApiService, protected stateService: StateService, private tokenService: TokenService, ) { this.activeAccountStatus$ = this.accountService.activeAccount$.pipe( map((account) => account?.id), switchMap((userId) => { return this.authStatusFor$(userId); }), ); this.authStatuses$ = this.accountService.accounts$.pipe( map((accounts) => Object.keys(accounts) as UserId[]), switchMap((entries) => { if (entries.length === 0) { return of([] as { userId: UserId; status: AuthenticationStatus }[]); } return combineLatest( entries.map((userId) => this.authStatusFor$(userId).pipe(map((status) => ({ userId, status }))), ), ); }), map((statuses) => { return statuses.reduce( (acc, { userId, status }) => { acc[userId] = status; return acc; }, {} as Record, ); }), ); } authStatusFor$(userId: UserId): Observable { if (!Utils.isGuid(userId)) { return of(AuthenticationStatus.LoggedOut); } return combineLatest([ this.cryptoService.getInMemoryUserKeyFor$(userId), this.tokenService.hasAccessToken$(userId), ]).pipe( map(([userKey, hasAccessToken]) => { if (!hasAccessToken) { return AuthenticationStatus.LoggedOut; } if (!userKey) { return AuthenticationStatus.Locked; } return AuthenticationStatus.Unlocked; }), distinctUntilChanged(), shareReplay({ bufferSize: 1, refCount: false }), ); } async getAuthStatus(userId?: string): Promise { userId ??= await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id))); return await firstValueFrom(this.authStatusFor$(userId as UserId)); } logOut(callback: () => void) { callback(); this.messagingService.send("loggedOut"); } }