1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-21 10:43:35 +00:00
Files
browser/libs/common/src/auth/services/auth.service.ts
Bernd Schoolmann 54cc35e29a [PM-6037] Fix process reload not triggering on inactive account lock/logout (#9805)
* Send loggedOut/locked events on logout/lock event

* Revert "Send loggedOut/locked events on logout/lock event"

This reverts commit 293f2d6131.

* Ensure loggedOut is sent for non-active user logouts too

* Make loggedOut accept userIds

* Add userBeingLoggedOut in desktop app component

* Await updateconnection calls
2024-09-13 18:11:05 +02:00

101 lines
3.1 KiB
TypeScript

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 { StateService } from "../../platform/abstractions/state.service";
import { MessageSender } from "../../platform/messaging";
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<AuthenticationStatus>;
authStatuses$: Observable<Record<UserId, AuthenticationStatus>>;
constructor(
protected accountService: AccountService,
protected messageSender: MessageSender,
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<UserId, AuthenticationStatus>,
);
}),
);
}
authStatusFor$(userId: UserId): Observable<AuthenticationStatus> {
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<AuthenticationStatus> {
userId ??= await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)));
return await firstValueFrom(this.authStatusFor$(userId as UserId));
}
logOut(callback: () => void, userId?: string): void {
callback();
this.messageSender.send("loggedOut", { userId });
}
}