1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 22:03:36 +00:00

[PM-5499] auth request service migrations (#8597)

* move auth request storage to service

* create migrations for auth requests

* fix tests

* fix browser

* fix login strategy

* update migration

* use correct test descriptions in migration
This commit is contained in:
Jake Fink
2024-04-15 12:34:30 -04:00
committed by GitHub
parent d0bcc75721
commit 576431d29e
26 changed files with 503 additions and 120 deletions

View File

@@ -33,6 +33,7 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { UserId } from "@bitwarden/common/types/guid";
import { CaptchaProtectedComponent } from "./captcha-protected.component";
@@ -131,6 +132,7 @@ export class LoginViaAuthRequestComponent
// This also prevents it from being lost on refresh as the
// login service email does not persist.
this.email = await this.stateService.getEmail();
const userId = (await firstValueFrom(this.accountService.activeAccount$)).id;
if (!this.email) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("userEmailMissing"));
@@ -142,10 +144,10 @@ export class LoginViaAuthRequestComponent
// We only allow a single admin approval request to be active at a time
// so must check state to see if we have an existing one or not
const adminAuthReqStorable = await this.stateService.getAdminAuthRequest();
const adminAuthReqStorable = await this.authRequestService.getAdminAuthRequest(userId);
if (adminAuthReqStorable) {
await this.handleExistingAdminAuthRequest(adminAuthReqStorable);
await this.handleExistingAdminAuthRequest(adminAuthReqStorable, userId);
} else {
// No existing admin auth request; so we need to create one
await this.startAuthRequestLogin();
@@ -173,7 +175,10 @@ export class LoginViaAuthRequestComponent
this.destroy$.complete();
}
private async handleExistingAdminAuthRequest(adminAuthReqStorable: AdminAuthRequestStorable) {
private async handleExistingAdminAuthRequest(
adminAuthReqStorable: AdminAuthRequestStorable,
userId: UserId,
) {
// Note: on login, the SSOLoginStrategy will also call to see an existing admin auth req
// has been approved and handle it if so.
@@ -183,13 +188,13 @@ export class LoginViaAuthRequestComponent
adminAuthReqResponse = await this.apiService.getAuthRequest(adminAuthReqStorable.id);
} catch (error) {
if (error instanceof ErrorResponse && error.statusCode === HttpStatusCode.NotFound) {
return await this.handleExistingAdminAuthReqDeletedOrDenied();
return await this.handleExistingAdminAuthReqDeletedOrDenied(userId);
}
}
// Request doesn't exist anymore
if (!adminAuthReqResponse) {
return await this.handleExistingAdminAuthReqDeletedOrDenied();
return await this.handleExistingAdminAuthReqDeletedOrDenied(userId);
}
// Re-derive the user's fingerprint phrase
@@ -203,7 +208,7 @@ export class LoginViaAuthRequestComponent
// Request denied
if (adminAuthReqResponse.isAnswered && !adminAuthReqResponse.requestApproved) {
return await this.handleExistingAdminAuthReqDeletedOrDenied();
return await this.handleExistingAdminAuthReqDeletedOrDenied(userId);
}
// Request approved
@@ -211,6 +216,7 @@ export class LoginViaAuthRequestComponent
return await this.handleApprovedAdminAuthRequest(
adminAuthReqResponse,
adminAuthReqStorable.privateKey,
userId,
);
}
@@ -219,9 +225,9 @@ export class LoginViaAuthRequestComponent
await this.anonymousHubService.createHubConnection(adminAuthReqStorable.id);
}
private async handleExistingAdminAuthReqDeletedOrDenied() {
private async handleExistingAdminAuthReqDeletedOrDenied(userId: UserId) {
// clear the admin auth request from state
await this.stateService.setAdminAuthRequest(null);
await this.authRequestService.clearAdminAuthRequest(userId);
// start new auth request
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
@@ -269,7 +275,8 @@ export class LoginViaAuthRequestComponent
privateKey: this.authRequestKeyPair.privateKey,
});
await this.stateService.setAdminAuthRequest(adminAuthReqStorable);
const userId = (await firstValueFrom(this.accountService.activeAccount$)).id;
await this.authRequestService.setAdminAuthRequest(adminAuthReqStorable, userId);
} else {
await this.buildAuthRequest(AuthRequestType.AuthenticateAndUnlock);
reqResponse = await this.apiService.postAuthRequest(this.authRequest);
@@ -333,9 +340,11 @@ export class LoginViaAuthRequestComponent
// if user has authenticated via SSO
if (this.userAuthNStatus === AuthenticationStatus.Locked) {
const userId = (await firstValueFrom(this.accountService.activeAccount$)).id;
return await this.handleApprovedAdminAuthRequest(
authReqResponse,
this.authRequestKeyPair.privateKey,
userId,
);
}
@@ -363,6 +372,7 @@ export class LoginViaAuthRequestComponent
async handleApprovedAdminAuthRequest(
adminAuthReqResponse: AuthRequestResponse,
privateKey: ArrayBuffer,
userId: UserId,
) {
// See verifyAndHandleApprovedAuthReq(...) for flow details
// it's flow 2 or 3 based on presence of masterPasswordHash
@@ -384,7 +394,7 @@ export class LoginViaAuthRequestComponent
// clear the admin auth request from state so it cannot be used again (it's a one time use)
// TODO: this should eventually be enforced via deleting this on the server once it is used
await this.stateService.setAdminAuthRequest(null);
await this.authRequestService.clearAdminAuthRequest(userId);
this.platformUtilsService.showToast("success", null, this.i18nService.t("loginApproved"));

View File

@@ -740,6 +740,7 @@ const safeProviders: SafeProvider[] = [
LOGOUT_CALLBACK,
StateServiceAbstraction,
AuthServiceAbstraction,
AuthRequestServiceAbstraction,
MessagingServiceAbstraction,
],
}),
@@ -963,6 +964,7 @@ const safeProviders: SafeProvider[] = [
InternalMasterPasswordServiceAbstraction,
CryptoServiceAbstraction,
ApiServiceAbstraction,
StateProvider,
],
}),
safeProvider({