1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 22:33:35 +00:00

[PM-5255, PM-3339] Refactor login strategy to use state providers (#7821)

* add key definition and StrategyData classes

* use state providers for login strategies

* serialize login data for cache

* use state providers for auth request notification

* fix registrations

* add docs to abstraction

* fix sso strategy

* fix password login strategy tests

* fix base login strategy tests

* fix user api login strategy tests

* PM-3339 add tests for admin auth request in sso strategy

* fix auth request login strategy tests

* fix webauthn login strategy tests

* create login strategy state

* use barrel file in common/spec

* test login strategy cache deserialization

* use global state provider

* add test for login strategy service

* fix auth request storage

* add recursive prototype checking and json deserializers to nested objects

* fix CLI

* Create wrapper for login strategy cache

* use behavior subjects in strategies instead of global state

* rename userApi to userApiKey

* pr feedback

* fix tests

* fix deserialization tests

* fix tests

---------

Co-authored-by: rr-bw <102181210+rr-bw@users.noreply.github.com>
This commit is contained in:
Jake Fink
2024-03-12 14:19:50 -04:00
committed by GitHub
parent 6b1da67f3a
commit a0e0637bb6
35 changed files with 1414 additions and 362 deletions

View File

@@ -99,8 +99,7 @@ export class LoginViaAuthRequestComponent
}
//gets signalR push notification
this.loginStrategyService
.getPushNotificationObs$()
this.loginStrategyService.authRequestPushNotification$
.pipe(takeUntil(this.destroy$))
.subscribe((id) => {
// Only fires on approval currently

View File

@@ -1,6 +1,7 @@
import { Directive, Inject, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute, NavigationExtras, Router } from "@angular/router";
import * as DuoWebSDK from "duo_web_sdk";
import { firstValueFrom } from "rxjs";
import { first } from "rxjs/operators";
// eslint-disable-next-line no-restricted-imports
@@ -10,6 +11,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-type";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
@@ -92,7 +94,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
}
async ngOnInit() {
if (!this.authing || this.twoFactorService.getProviders() == null) {
if (!(await this.authing()) || this.twoFactorService.getProviders() == null) {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate([this.loginRoute]);
@@ -105,7 +107,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
}
});
if (this.needsLock) {
if (await this.needsLock()) {
this.successRoute = "lock";
}
@@ -426,7 +428,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
return;
}
if (this.loginStrategyService.email == null) {
if ((await this.loginStrategyService.getEmail()) == null) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
@@ -437,12 +439,13 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
try {
const request = new TwoFactorEmailRequest();
request.email = this.loginStrategyService.email;
request.masterPasswordHash = this.loginStrategyService.masterPasswordHash;
request.ssoEmail2FaSessionToken = this.loginStrategyService.ssoEmail2FaSessionToken;
request.email = await this.loginStrategyService.getEmail();
request.masterPasswordHash = await this.loginStrategyService.getMasterPasswordHash();
request.ssoEmail2FaSessionToken =
await this.loginStrategyService.getSsoEmail2FaSessionToken();
request.deviceIdentifier = await this.appIdService.getAppId();
request.authRequestAccessCode = this.loginStrategyService.accessCode;
request.authRequestId = this.loginStrategyService.authRequestId;
request.authRequestAccessCode = await this.loginStrategyService.getAccessCode();
request.authRequestId = await this.loginStrategyService.getAuthRequestId();
this.emailPromise = this.apiService.postTwoFactorEmail(request);
await this.emailPromise;
if (doToast) {
@@ -476,20 +479,13 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
}
}
get authing(): boolean {
return (
this.loginStrategyService.authingWithPassword() ||
this.loginStrategyService.authingWithSso() ||
this.loginStrategyService.authingWithUserApiKey() ||
this.loginStrategyService.authingWithPasswordless()
);
private async authing(): Promise<boolean> {
return (await firstValueFrom(this.loginStrategyService.currentAuthType$)) !== null;
}
get needsLock(): boolean {
return (
this.loginStrategyService.authingWithSso() ||
this.loginStrategyService.authingWithUserApiKey()
);
private async needsLock(): Promise<boolean> {
const authType = await firstValueFrom(this.loginStrategyService.currentAuthType$);
return authType == AuthenticationType.Sso || authType == AuthenticationType.UserApiKey;
}
// implemented in clients

View File

@@ -328,6 +328,7 @@ import { ModalService } from "./modal.service";
PolicyServiceAbstraction,
DeviceTrustCryptoServiceAbstraction,
AuthRequestServiceAbstraction,
GlobalStateProvider,
],
},
{