mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-21 16:18:28 +01:00
[PM-4650] Provide user interaction for adding domain to excluded domains for passkeys (#7041)
* Added new locales text * expose the sender url to be used in the use browser link component * Modified use browser link to have a dropdown of two options, just once or always for this site * modified component to use the use browser link component * refactored method * Made style changes and also updated the windows popout height * ran prettier * corrected google domain * [PM-5281] [PM-5282] Disable User Interaction Post 'Always for this Site' Selection and Preserve Prior Exclusions (#7237) * Added new domain alongside existing domains when saving to state * Added an overlay whne user clicks always for this site to prevent further interaction on the page * changed opacity * moved overlay to fido2-use-browser-link * removed private method and renamed variable
This commit is contained in:
parent
8036113e46
commit
c1d856430a
@ -1725,7 +1725,7 @@
|
|||||||
"placeholders": {
|
"placeholders": {
|
||||||
"domain": {
|
"domain": {
|
||||||
"content": "$1",
|
"content": "$1",
|
||||||
"example": "googlecom"
|
"example": "google.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2727,9 +2727,6 @@
|
|||||||
"yourPasskeyIsLocked": {
|
"yourPasskeyIsLocked": {
|
||||||
"message": "Authentication required to use passkey. Verify your identity to continue."
|
"message": "Authentication required to use passkey. Verify your identity to continue."
|
||||||
},
|
},
|
||||||
"useBrowserName": {
|
|
||||||
"message": "Use browser"
|
|
||||||
},
|
|
||||||
"multifactorAuthenticationCancelled": {
|
"multifactorAuthenticationCancelled": {
|
||||||
"message": "Multifactor authentication cancelled"
|
"message": "Multifactor authentication cancelled"
|
||||||
},
|
},
|
||||||
@ -2823,5 +2820,23 @@
|
|||||||
},
|
},
|
||||||
"hostedAt": {
|
"hostedAt": {
|
||||||
"message": "hosted at"
|
"message": "hosted at"
|
||||||
|
},
|
||||||
|
"useDeviceOrHardwareKey": {
|
||||||
|
"message": "Use your device or hardware key"
|
||||||
|
},
|
||||||
|
"justOnce": {
|
||||||
|
"message": "Just once"
|
||||||
|
},
|
||||||
|
"alwaysForThisSite": {
|
||||||
|
"message": "Always for this site"
|
||||||
|
},
|
||||||
|
"domainAddedToExcludedDomains": {
|
||||||
|
"message": "$DOMAIN$ added to excluded domains.",
|
||||||
|
"placeholders": {
|
||||||
|
"domain": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "google.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,14 +153,6 @@ body.body-full {
|
|||||||
margin: 15px 0 15px 0;
|
margin: 15px 0 15px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.useBrowserlink {
|
|
||||||
padding: 0 10px 5px 10px;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 10px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
app-options {
|
app-options {
|
||||||
.box {
|
.box {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
@ -184,6 +176,52 @@ app-vault-attachments {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.useBrowserlink {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: $font-size-small;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fido2-browser-selector-dropdown {
|
||||||
|
@include themify($themes) {
|
||||||
|
background-color: themed("boxBackgroundColor");
|
||||||
|
}
|
||||||
|
padding: 8px;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow:
|
||||||
|
0 2px 2px 0 rgba(0, 0, 0, 0.14),
|
||||||
|
0 3px 1px -2px rgba(0, 0, 0, 0.12),
|
||||||
|
0 1px 5px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: $border-radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fido2-browser-selector-dropdown-item {
|
||||||
|
@include themify($themes) {
|
||||||
|
color: themed("textColor") !important;
|
||||||
|
}
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
padding: 0px 15px 0px 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@include themify($themes) {
|
||||||
|
background-color: themed("listItemBackgroundHoverColor") !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
app-fido2 {
|
app-fido2 {
|
||||||
.auth-wrapper {
|
.auth-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -266,7 +304,6 @@ app-fido2 {
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
margin-bottom: 32px;
|
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
font-family: Open Sans;
|
font-family: Open Sans;
|
||||||
|
@ -46,6 +46,7 @@ export function fido2PopoutSessionData$() {
|
|||||||
sessionId: queryParams.sessionId as string,
|
sessionId: queryParams.sessionId as string,
|
||||||
fallbackSupported: queryParams.fallbackSupported === "true",
|
fallbackSupported: queryParams.fallbackSupported === "true",
|
||||||
userVerification: queryParams.userVerification === "true",
|
userVerification: queryParams.userVerification === "true",
|
||||||
|
senderUrl: queryParams.senderUrl as string,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,52 @@
|
|||||||
<div class="useBrowserlink" *ngIf="(fido2PopoutSessionData$ | async).fallbackSupported">
|
<ng-container *ngIf="(fido2PopoutSessionData$ | async).fallbackSupported">
|
||||||
<button appStopClick type="button" (click)="abort()">
|
<div class="useBrowserlink">
|
||||||
{{ "useBrowserName" | i18n }}
|
<button
|
||||||
</button>
|
type="button"
|
||||||
</div>
|
(click)="toggle()"
|
||||||
|
cdkOverlayOrigin
|
||||||
|
#trigger="cdkOverlayOrigin"
|
||||||
|
aria-haspopup="dialog"
|
||||||
|
aria-controls="cdk-overlay-container"
|
||||||
|
>
|
||||||
|
<span class="text-primary">
|
||||||
|
{{ "useDeviceOrHardwareKey" | i18n }}
|
||||||
|
</span>
|
||||||
|
<i class="bwi bwi-fw bwi-sm bwi-angle-down" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template
|
||||||
|
cdkConnectedOverlay
|
||||||
|
[cdkConnectedOverlayOrigin]="trigger"
|
||||||
|
[cdkConnectedOverlayOpen]="isOpen"
|
||||||
|
[cdkConnectedOverlayPositions]="overlayPosition"
|
||||||
|
[cdkConnectedOverlayHasBackdrop]="true"
|
||||||
|
[cdkConnectedOverlayBackdropClass]="'cdk-overlay-transparent-backdrop'"
|
||||||
|
(backdropClick)="isOpen = false"
|
||||||
|
(detach)="close()"
|
||||||
|
>
|
||||||
|
<div class="box-content">
|
||||||
|
<div
|
||||||
|
class="fido2-browser-selector-dropdown"
|
||||||
|
[@transformPanel]="'open'"
|
||||||
|
cdkTrapFocus
|
||||||
|
cdkTrapFocusAutoCapture
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
>
|
||||||
|
<button type="button" class="fido2-browser-selector-dropdown-item" (click)="abort(false)">
|
||||||
|
<span>{{ "justOnce" | i18n }}</span>
|
||||||
|
</button>
|
||||||
|
<br />
|
||||||
|
<button type="button" class="fido2-browser-selector-dropdown-item" (click)="abort()">
|
||||||
|
<span>{{ "alwaysForThisSite" | i18n }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div
|
||||||
|
*ngIf="showOverlay"
|
||||||
|
class="tw-absolute tw-w-full tw-h-full tw-bg-background-alt tw-inset-0 tw-bg-opacity-80 tw-z-50"
|
||||||
|
></div>
|
||||||
|
</ng-container>
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
|
import { animate, state, style, transition, trigger } from "@angular/animations";
|
||||||
|
import { ConnectedPosition } from "@angular/cdk/overlay";
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BrowserFido2UserInterfaceSession,
|
BrowserFido2UserInterfaceSession,
|
||||||
fido2PopoutSessionData$,
|
fido2PopoutSessionData$,
|
||||||
@ -9,13 +16,99 @@ import {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: "app-fido2-use-browser-link",
|
selector: "app-fido2-use-browser-link",
|
||||||
templateUrl: "fido2-use-browser-link.component.html",
|
templateUrl: "fido2-use-browser-link.component.html",
|
||||||
|
animations: [
|
||||||
|
trigger("transformPanel", [
|
||||||
|
state(
|
||||||
|
"void",
|
||||||
|
style({
|
||||||
|
opacity: 0,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
transition(
|
||||||
|
"void => open",
|
||||||
|
animate(
|
||||||
|
"100ms linear",
|
||||||
|
style({
|
||||||
|
opacity: 1,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
transition("* => void", animate("100ms linear", style({ opacity: 0 }))),
|
||||||
|
]),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class Fido2UseBrowserLinkComponent {
|
export class Fido2UseBrowserLinkComponent {
|
||||||
|
showOverlay: boolean = false;
|
||||||
|
isOpen = false;
|
||||||
|
overlayPosition: ConnectedPosition[] = [
|
||||||
|
{
|
||||||
|
originX: "start",
|
||||||
|
originY: "bottom",
|
||||||
|
overlayX: "start",
|
||||||
|
overlayY: "top",
|
||||||
|
offsetY: 5,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
protected fido2PopoutSessionData$ = fido2PopoutSessionData$();
|
protected fido2PopoutSessionData$ = fido2PopoutSessionData$();
|
||||||
|
|
||||||
protected async abort() {
|
constructor(
|
||||||
|
private stateService: StateService,
|
||||||
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
this.isOpen = !this.isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.isOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aborts the current FIDO2 session and fallsback to the browser.
|
||||||
|
* @param excludeDomain - Identifies if the domain should be excluded from future FIDO2 prompts.
|
||||||
|
*/
|
||||||
|
protected async abort(excludeDomain = true) {
|
||||||
|
this.close();
|
||||||
const sessionData = await firstValueFrom(this.fido2PopoutSessionData$);
|
const sessionData = await firstValueFrom(this.fido2PopoutSessionData$);
|
||||||
BrowserFido2UserInterfaceSession.abortPopout(sessionData.sessionId, true);
|
|
||||||
return;
|
if (!excludeDomain) {
|
||||||
|
this.abortSession(sessionData.sessionId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Show overlay to prevent the user from interacting with the page.
|
||||||
|
this.showOverlay = true;
|
||||||
|
await this.handleDomainExclusion(sessionData.senderUrl);
|
||||||
|
// Give the user a chance to see the toast before closing the popout.
|
||||||
|
await Utils.delay(2000);
|
||||||
|
this.abortSession(sessionData.sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Excludes the domain from future FIDO2 prompts.
|
||||||
|
* @param uri - The domain uri to exclude from future FIDO2 prompts.
|
||||||
|
*/
|
||||||
|
private async handleDomainExclusion(uri: string) {
|
||||||
|
const exisitingDomains = await this.stateService.getNeverDomains();
|
||||||
|
|
||||||
|
const validDomain = Utils.getHostname(uri);
|
||||||
|
const savedDomains: { [name: string]: unknown } = {
|
||||||
|
...exisitingDomains,
|
||||||
|
};
|
||||||
|
savedDomains[validDomain] = null;
|
||||||
|
|
||||||
|
this.stateService.setNeverDomains(savedDomains);
|
||||||
|
|
||||||
|
this.platformUtilsService.showToast(
|
||||||
|
"success",
|
||||||
|
null,
|
||||||
|
this.i18nService.t("domainAddedToExcludedDomains", validDomain),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private abortSession(sessionId: string) {
|
||||||
|
BrowserFido2UserInterfaceSession.abortPopout(sessionId, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,10 +130,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<div class="useBrowserlink">
|
|
||||||
<button *ngIf="data.fallbackSupported" appStopClick type="button" (click)="abort(true)">
|
<app-fido2-use-browser-link></app-fido2-use-browser-link>
|
||||||
{{ "useBrowserName" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -49,8 +49,6 @@ interface ViewData {
|
|||||||
export class Fido2Component implements OnInit, OnDestroy {
|
export class Fido2Component implements OnInit, OnDestroy {
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
private hasSearched = false;
|
private hasSearched = false;
|
||||||
private searchTimeout: any = null;
|
|
||||||
private hasLoadedAllCiphers = false;
|
|
||||||
|
|
||||||
protected cipher: CipherView;
|
protected cipher: CipherView;
|
||||||
protected searchTypeSearch = false;
|
protected searchTypeSearch = false;
|
||||||
|
@ -161,7 +161,7 @@ describe("VaultPopoutWindow", () => {
|
|||||||
singleActionKey: `${VaultPopoutType.fido2Popout}_sessionId`,
|
singleActionKey: `${VaultPopoutType.fido2Popout}_sessionId`,
|
||||||
senderWindowId: 1,
|
senderWindowId: 1,
|
||||||
forceCloseExistingWindows: true,
|
forceCloseExistingWindows: true,
|
||||||
windowOptions: { height: 450 },
|
windowOptions: { height: 570 },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
expect(returnedWindowId).toEqual(10);
|
expect(returnedWindowId).toEqual(10);
|
||||||
|
@ -154,7 +154,7 @@ async function openFido2Popout(
|
|||||||
singleActionKey: `${VaultPopoutType.fido2Popout}_${sessionId}`,
|
singleActionKey: `${VaultPopoutType.fido2Popout}_${sessionId}`,
|
||||||
senderWindowId: senderTab.windowId,
|
senderWindowId: senderTab.windowId,
|
||||||
forceCloseExistingWindows: true,
|
forceCloseExistingWindows: true,
|
||||||
windowOptions: { height: 450 },
|
windowOptions: { height: 570 },
|
||||||
});
|
});
|
||||||
|
|
||||||
return popoutWindow.id;
|
return popoutWindow.id;
|
||||||
|
Loading…
Reference in New Issue
Block a user