mirror of
https://github.com/bitwarden/browser.git
synced 2025-03-11 13:30:39 +01:00
[PM-4690] Setting in the browser extension that turns off passkeys (#6929)
* use passkeys setting * check state service on isFido2FeatureEnabled * fix broken json * update description text * make setting global * invert logic to positive state * fix and add to fido2 client service tests
This commit is contained in:
parent
59f1a2d022
commit
74208d568e
@ -657,6 +657,12 @@
|
||||
"changedPasswordNotificationDescAlt": {
|
||||
"message": "Ask to update a login's password when a change is detected on a website. Applies to all logged in accounts."
|
||||
},
|
||||
"enableUsePasskeys": {
|
||||
"message": "Ask to save and use passkeys"
|
||||
},
|
||||
"usePasskeysDesc": {
|
||||
"message": "Ask to save new passkeys or log in with passkeys stored in your vault. Applies to all logged in accounts."
|
||||
},
|
||||
"notificationChangeDesc": {
|
||||
"message": "Do you want to update this password in Bitwarden?"
|
||||
},
|
||||
|
@ -104,6 +104,23 @@
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="use-passkeys">{{ "enableUsePasskeys" | i18n }}</label>
|
||||
<input
|
||||
id="use-passkeys"
|
||||
type="checkbox"
|
||||
aria-describedby="use-passkeysHelp"
|
||||
(change)="updateEnablePasskeys()"
|
||||
[(ngModel)]="enablePasskeys"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="use-passkeysHelp" class="box-footer">
|
||||
{{ "usePasskeysDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
|
@ -24,6 +24,7 @@ export class OptionsComponent implements OnInit {
|
||||
enableContextMenuItem = false;
|
||||
enableAddLoginNotification = false;
|
||||
enableChangedPasswordNotification = false;
|
||||
enablePasskeys = true;
|
||||
showCardsCurrentTab = false;
|
||||
showIdentitiesCurrentTab = false;
|
||||
showClearClipboard = true;
|
||||
@ -100,6 +101,8 @@ export class OptionsComponent implements OnInit {
|
||||
|
||||
this.enableBadgeCounter = !(await this.stateService.getDisableBadgeCounter());
|
||||
|
||||
this.enablePasskeys = await this.stateService.getEnablePasskeys();
|
||||
|
||||
this.theme = await this.stateService.getTheme();
|
||||
|
||||
const defaultUriMatch = await this.stateService.getDefaultUriMatch();
|
||||
@ -118,6 +121,10 @@ export class OptionsComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
async updateEnablePasskeys() {
|
||||
await this.stateService.setEnablePasskeys(this.enablePasskeys);
|
||||
}
|
||||
|
||||
async updateContextMenuItem() {
|
||||
await this.stateService.setDisableContextMenuItem(!this.enableContextMenuItem);
|
||||
this.messagingService.send("bgUpdateContextMenu");
|
||||
|
@ -243,6 +243,8 @@ export abstract class StateService<T extends Account = Account> {
|
||||
value: boolean,
|
||||
options?: StorageOptions
|
||||
) => Promise<void>;
|
||||
getEnablePasskeys: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnablePasskeys: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDisableContextMenuItem: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableContextMenuItem: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
/**
|
||||
|
@ -36,6 +36,7 @@ export class GlobalState {
|
||||
enableDuckDuckGoBrowserIntegration?: boolean;
|
||||
region?: string;
|
||||
neverDomains?: { [id: string]: unknown };
|
||||
enablePasskeys?: boolean;
|
||||
disableAddLoginNotification?: boolean;
|
||||
disableChangedPasswordNotification?: boolean;
|
||||
disableContextMenuItem?: boolean;
|
||||
|
@ -1213,6 +1213,24 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
async getEnablePasskeys(options?: StorageOptions): Promise<boolean> {
|
||||
return (
|
||||
(await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())))
|
||||
?.enablePasskeys ?? true
|
||||
);
|
||||
}
|
||||
|
||||
async setEnablePasskeys(value: boolean, options?: StorageOptions): Promise<void> {
|
||||
const globals = await this.getGlobals(
|
||||
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
||||
);
|
||||
globals.enablePasskeys = value;
|
||||
await this.saveGlobals(
|
||||
globals,
|
||||
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
||||
);
|
||||
}
|
||||
|
||||
async getDisableContextMenuItem(options?: StorageOptions): Promise<boolean> {
|
||||
return (
|
||||
(await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())))
|
||||
|
@ -40,6 +40,7 @@ describe("FidoAuthenticatorService", () => {
|
||||
|
||||
client = new Fido2ClientService(authenticator, configService, authService, stateService);
|
||||
configService.getFeatureFlag.mockResolvedValue(true);
|
||||
stateService.getEnablePasskeys.mockResolvedValue(true);
|
||||
tab = { id: 123, windowId: 456 } as chrome.tabs.Tab;
|
||||
});
|
||||
|
||||
@ -229,6 +230,16 @@ describe("FidoAuthenticatorService", () => {
|
||||
await rejects.toThrow(FallbackRequestedError);
|
||||
});
|
||||
|
||||
it("should throw FallbackRequestedError if passkeys state is not enabled", async () => {
|
||||
const params = createParams();
|
||||
stateService.getEnablePasskeys.mockResolvedValue(false);
|
||||
|
||||
const result = async () => await client.createCredential(params, tab);
|
||||
|
||||
const rejects = expect(result).rejects;
|
||||
await rejects.toThrow(FallbackRequestedError);
|
||||
});
|
||||
|
||||
it("should throw FallbackRequestedError if user is logged out", async () => {
|
||||
const params = createParams();
|
||||
authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.LoggedOut);
|
||||
@ -389,6 +400,16 @@ describe("FidoAuthenticatorService", () => {
|
||||
await rejects.toThrow(FallbackRequestedError);
|
||||
});
|
||||
|
||||
it("should throw FallbackRequestedError if passkeys state is not enabled", async () => {
|
||||
const params = createParams();
|
||||
stateService.getEnablePasskeys.mockResolvedValue(false);
|
||||
|
||||
const result = async () => await client.assertCredential(params, tab);
|
||||
|
||||
const rejects = expect(result).rejects;
|
||||
await rejects.toThrow(FallbackRequestedError);
|
||||
});
|
||||
|
||||
it("should throw FallbackRequestedError if user is logged out", async () => {
|
||||
const params = createParams();
|
||||
authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.LoggedOut);
|
||||
|
@ -46,7 +46,11 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
|
||||
) {}
|
||||
|
||||
async isFido2FeatureEnabled(): Promise<boolean> {
|
||||
return await this.configService.getFeatureFlag<boolean>(FeatureFlag.Fido2VaultCredentials);
|
||||
const featureFlagEnabled = await this.configService.getFeatureFlag<boolean>(
|
||||
FeatureFlag.Fido2VaultCredentials
|
||||
);
|
||||
const userEnabledPasskeys = await this.stateService.getEnablePasskeys();
|
||||
return featureFlagEnabled && userEnabledPasskeys;
|
||||
}
|
||||
|
||||
async createCredential(
|
||||
|
Loading…
Reference in New Issue
Block a user