1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-01-31 22:51:28 +01:00

[PM-1500] Add feature flag to enable passkeys (#5406)

* Added launch darkly feature flag to passkeys implementation

* fixed linter

* Updated fido2 client service test to accomodate feature flag

* Updated fido2client service to include unit test for feature flag

* Renamed enable pass keys to fido2 vault credentials, added unit test when feature flag is not enabled

* fixed failing Login domain test case
This commit is contained in:
SmithThe4th 2023-05-24 08:06:41 -04:00 committed by GitHub
parent 08405d6b7d
commit c14ab487ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 3 deletions

View File

@ -56,6 +56,7 @@ import { AvatarUpdateService } from "@bitwarden/common/services/account/avatar-u
import { ApiService } from "@bitwarden/common/services/api.service";
import { AppIdService } from "@bitwarden/common/services/appId.service";
import { AuditService } from "@bitwarden/common/services/audit.service";
import { ConfigApiService } from "@bitwarden/common/services/config/config-api.service";
import { ConfigService } from "@bitwarden/common/services/config/config.service";
import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service";
import { ContainerService } from "@bitwarden/common/services/container.service";
@ -509,6 +510,8 @@ export default class MainBackground {
this.userVerificationApiService
);
this.configApiService = new ConfigApiService(this.apiService);
this.configService = new ConfigService(
this.stateService,
this.configApiService,
@ -524,6 +527,7 @@ export default class MainBackground {
);
this.fido2ClientService = new Fido2ClientService(
this.fido2AuthenticatorService,
this.configService,
this.logService
);

View File

@ -2,7 +2,6 @@ import { Observable } from "rxjs";
import { DeviceType } from "@bitwarden/common/enums/device-type.enum";
import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service";
import { TabMessage } from "../types/tab-messages";

View File

@ -1,5 +1,6 @@
export enum FeatureFlag {
DisplayEuEnvironmentFlag = "display-eu-environment",
DisplayLowKdfIterationWarningFlag = "display-kdf-iteration-warning",
Fido2VaultCredentials = "fido2-vault-credentials",
TrustedDeviceEncryption = "trusted-device-encryption",
}

View File

@ -1,5 +1,6 @@
import { mock, MockProxy } from "jest-mock-extended";
import { ConfigServiceAbstraction } from "../../abstractions/config/config.service.abstraction";
import { Utils } from "../../misc/utils";
import {
Fido2AutenticatorError,
@ -10,6 +11,7 @@ import {
import {
AssertCredentialParams,
CreateCredentialParams,
FallbackRequestedError,
} from "../abstractions/fido2-client.service.abstraction";
import { Fido2Utils } from "../abstractions/fido2-utils";
@ -20,11 +22,14 @@ const RpId = "bitwarden.com";
describe("FidoAuthenticatorService", () => {
let authenticator!: MockProxy<Fido2AuthenticatorService>;
let configService!: MockProxy<ConfigServiceAbstraction>;
let client!: Fido2ClientService;
beforeEach(async () => {
authenticator = mock<Fido2AuthenticatorService>();
client = new Fido2ClientService(authenticator);
configService = mock<ConfigServiceAbstraction>();
client = new Fido2ClientService(authenticator, configService);
configService.getFeatureFlagBool.mockResolvedValue(true);
});
describe("createCredential", () => {
@ -188,6 +193,16 @@ describe("FidoAuthenticatorService", () => {
await rejects.toMatchObject({ name: "NotAllowedError" });
await rejects.toBeInstanceOf(DOMException);
});
it("should throw FallbackRequestedError if feature flag is not enabled", async () => {
const params = createParams();
configService.getFeatureFlagBool.mockResolvedValue(false);
const result = async () => await client.createCredential(params);
const rejects = expect(result).rejects;
await rejects.toThrow(FallbackRequestedError);
});
});
function createParams(params: Partial<CreateCredentialParams> = {}): CreateCredentialParams {
@ -315,6 +330,16 @@ describe("FidoAuthenticatorService", () => {
await rejects.toMatchObject({ name: "NotAllowedError" });
await rejects.toBeInstanceOf(DOMException);
});
it("should throw FallbackRequestedError if feature flag is not enabled", async () => {
const params = createParams();
configService.getFeatureFlagBool.mockResolvedValue(false);
const result = async () => await client.assertCredential(params);
const rejects = expect(result).rejects;
await rejects.toThrow(FallbackRequestedError);
});
});
describe("assert non-discoverable credential", () => {

View File

@ -1,6 +1,8 @@
import { parse } from "tldts";
import { ConfigServiceAbstraction } from "../../abstractions/config/config.service.abstraction";
import { LogService } from "../../abstractions/log.service";
import { FeatureFlag } from "../../enums/feature-flag.enum";
import { Utils } from "../../misc/utils";
import {
Fido2AutenticatorError,
@ -26,12 +28,25 @@ import { Fido2Utils } from "../abstractions/fido2-utils";
import { isValidRpId } from "./domain-utils";
export class Fido2ClientService implements Fido2ClientServiceAbstraction {
constructor(private authenticator: Fido2AuthenticatorService, private logService?: LogService) {}
constructor(
private authenticator: Fido2AuthenticatorService,
private configService: ConfigServiceAbstraction,
private logService?: LogService
) {}
async createCredential(
params: CreateCredentialParams,
abortController = new AbortController()
): Promise<CreateCredentialResult> {
const enableFido2VaultCredentials = await this.configService.getFeatureFlagBool(
FeatureFlag.Fido2VaultCredentials
);
if (!enableFido2VaultCredentials) {
this.logService?.warning(`[Fido2Client] Fido2VaultCredential is not enabled`);
throw new FallbackRequestedError();
}
if (!params.sameOriginWithAncestors) {
this.logService?.warning(
`[Fido2Client] Invalid 'sameOriginWithAncestors' value: ${params.sameOriginWithAncestors}`
@ -176,6 +191,15 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
params: AssertCredentialParams,
abortController = new AbortController()
): Promise<AssertCredentialResult> {
const enableFido2VaultCredentials = await this.configService.getFeatureFlagBool(
FeatureFlag.Fido2VaultCredentials
);
if (!enableFido2VaultCredentials) {
this.logService?.warning(`[Fido2Client] Fido2VaultCredential is not enabled`);
throw new FallbackRequestedError();
}
const { domain: effectiveDomain } = parse(params.origin, { allowPrivateDomains: true });
if (effectiveDomain == undefined) {
this.logService?.warning(`[Fido2Client] Invalid origin: ${params.origin}`);

View File

@ -112,6 +112,19 @@ describe("Login DTO", () => {
password: "myPassword",
passwordRevisionDate: passwordRevisionDate.toISOString(),
totp: "myTotp",
fido2Key: {
nonDiscoverableId: "keyId",
keyType: "keyType",
keyAlgorithm: "keyAlgorithm",
keyCurve: "keyCurve",
keyValue: "keyValue",
rpId: "rpId",
userHandle: "userHandle",
counter: "counter",
rpName: "rpName",
userName: "userName",
origin: "origin",
},
});
expect(actual).toEqual({
@ -120,6 +133,19 @@ describe("Login DTO", () => {
password: "myPassword_fromJSON",
passwordRevisionDate: passwordRevisionDate,
totp: "myTotp_fromJSON",
fido2Key: {
nonDiscoverableId: "keyId_fromJSON",
keyType: "keyType_fromJSON",
keyAlgorithm: "keyAlgorithm_fromJSON",
keyCurve: "keyCurve_fromJSON",
keyValue: "keyValue_fromJSON",
rpId: "rpId_fromJSON",
userHandle: "userHandle_fromJSON",
counter: "counter_fromJSON",
rpName: "rpName_fromJSON",
userName: "userName_fromJSON",
origin: "origin_fromJSON",
},
});
expect(actual).toBeInstanceOf(Login);
});