mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-22 16:29:09 +01:00
Add shared webauthn component (#9771)
This commit is contained in:
parent
96538a68fd
commit
69a37a884f
@ -611,6 +611,9 @@
|
|||||||
"verificationCodeRequired": {
|
"verificationCodeRequired": {
|
||||||
"message": "Verification code is required."
|
"message": "Verification code is required."
|
||||||
},
|
},
|
||||||
|
"webauthnCancelOrTimeout": {
|
||||||
|
"message": "The authentication was cancelled or took too long. Please try again."
|
||||||
|
},
|
||||||
"invalidVerificationCode": {
|
"invalidVerificationCode": {
|
||||||
"message": "Invalid verification code"
|
"message": "Invalid verification code"
|
||||||
},
|
},
|
||||||
|
@ -4,6 +4,7 @@ import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
|
|||||||
import { ActivatedRoute, Router, RouterLink } from "@angular/router";
|
import { ActivatedRoute, Router, RouterLink } from "@angular/router";
|
||||||
|
|
||||||
import { TwoFactorAuthAuthenticatorComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-auth-authenticator.component";
|
import { TwoFactorAuthAuthenticatorComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-auth-authenticator.component";
|
||||||
|
import { TwoFactorAuthWebAuthnComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-auth-webauthn.component";
|
||||||
import { TwoFactorAuthYubikeyComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-auth-yubikey.component";
|
import { TwoFactorAuthYubikeyComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-auth-yubikey.component";
|
||||||
import { TwoFactorAuthComponent as BaseTwoFactorAuthComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-auth.component";
|
import { TwoFactorAuthComponent as BaseTwoFactorAuthComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-auth.component";
|
||||||
import { TwoFactorOptionsComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-options.component";
|
import { TwoFactorOptionsComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-options.component";
|
||||||
@ -64,6 +65,7 @@ import { TwoFactorAuthEmailComponent } from "./two-factor-auth-email.component";
|
|||||||
TwoFactorAuthEmailComponent,
|
TwoFactorAuthEmailComponent,
|
||||||
TwoFactorAuthAuthenticatorComponent,
|
TwoFactorAuthAuthenticatorComponent,
|
||||||
TwoFactorAuthYubikeyComponent,
|
TwoFactorAuthYubikeyComponent,
|
||||||
|
TwoFactorAuthWebAuthnComponent,
|
||||||
],
|
],
|
||||||
providers: [I18nPipe],
|
providers: [I18nPipe],
|
||||||
})
|
})
|
||||||
|
@ -6,6 +6,7 @@ import { RouterLink } from "@angular/router";
|
|||||||
|
|
||||||
import { TwoFactorAuthAuthenticatorComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-authenticator.component";
|
import { TwoFactorAuthAuthenticatorComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-authenticator.component";
|
||||||
import { TwoFactorAuthEmailComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component";
|
import { TwoFactorAuthEmailComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component";
|
||||||
|
import { TwoFactorAuthWebAuthnComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-webauthn.component";
|
||||||
import { TwoFactorAuthYubikeyComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-yubikey.component";
|
import { TwoFactorAuthYubikeyComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-yubikey.component";
|
||||||
import { TwoFactorAuthComponent as BaseTwoFactorAuthComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component";
|
import { TwoFactorAuthComponent as BaseTwoFactorAuthComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component";
|
||||||
import { TwoFactorOptionsComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-options.component";
|
import { TwoFactorOptionsComponent } from "../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-options.component";
|
||||||
@ -39,6 +40,7 @@ import { TypographyModule } from "../../../../libs/components/src/typography";
|
|||||||
TwoFactorAuthEmailComponent,
|
TwoFactorAuthEmailComponent,
|
||||||
TwoFactorAuthAuthenticatorComponent,
|
TwoFactorAuthAuthenticatorComponent,
|
||||||
TwoFactorAuthYubikeyComponent,
|
TwoFactorAuthYubikeyComponent,
|
||||||
|
TwoFactorAuthWebAuthnComponent,
|
||||||
],
|
],
|
||||||
providers: [I18nPipe],
|
providers: [I18nPipe],
|
||||||
})
|
})
|
||||||
|
@ -627,6 +627,9 @@
|
|||||||
"verificationCodeRequired": {
|
"verificationCodeRequired": {
|
||||||
"message": "Verification code is required."
|
"message": "Verification code is required."
|
||||||
},
|
},
|
||||||
|
"webauthnCancelOrTimeout": {
|
||||||
|
"message": "The authentication was cancelled or took too long. Please try again."
|
||||||
|
},
|
||||||
"invalidVerificationCode": {
|
"invalidVerificationCode": {
|
||||||
"message": "Invalid verification code"
|
"message": "Invalid verification code"
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,7 @@ import { LinkModule, TypographyModule, CheckboxModule, DialogService } from "@bi
|
|||||||
|
|
||||||
import { TwoFactorAuthAuthenticatorComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-authenticator.component";
|
import { TwoFactorAuthAuthenticatorComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-authenticator.component";
|
||||||
import { TwoFactorAuthEmailComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component";
|
import { TwoFactorAuthEmailComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component";
|
||||||
|
import { TwoFactorAuthWebAuthnComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-webauthn.component";
|
||||||
import { TwoFactorAuthYubikeyComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-yubikey.component";
|
import { TwoFactorAuthYubikeyComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-yubikey.component";
|
||||||
import { TwoFactorAuthComponent as BaseTwoFactorAuthComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component";
|
import { TwoFactorAuthComponent as BaseTwoFactorAuthComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component";
|
||||||
import { TwoFactorOptionsComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-options.component";
|
import { TwoFactorOptionsComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-options.component";
|
||||||
@ -54,6 +55,7 @@ import { FormFieldModule } from "../../../../../libs/components/src/form-field";
|
|||||||
TwoFactorAuthEmailComponent,
|
TwoFactorAuthEmailComponent,
|
||||||
TwoFactorAuthAuthenticatorComponent,
|
TwoFactorAuthAuthenticatorComponent,
|
||||||
TwoFactorAuthYubikeyComponent,
|
TwoFactorAuthYubikeyComponent,
|
||||||
|
TwoFactorAuthWebAuthnComponent,
|
||||||
],
|
],
|
||||||
providers: [I18nPipe],
|
providers: [I18nPipe],
|
||||||
})
|
})
|
||||||
|
@ -5519,6 +5519,9 @@
|
|||||||
"verificationCodeRequired": {
|
"verificationCodeRequired": {
|
||||||
"message": "Verification code is required."
|
"message": "Verification code is required."
|
||||||
},
|
},
|
||||||
|
"webauthnCancelOrTimeout": {
|
||||||
|
"message": "The authentication was cancelled or took too long. Please try again."
|
||||||
|
},
|
||||||
"invalidVerificationCode": {
|
"invalidVerificationCode": {
|
||||||
"message": "Invalid verification code"
|
"message": "Invalid verification code"
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
<div id="web-authn-frame" class="tw-mb-3" *ngIf="!webAuthnNewTab">
|
||||||
|
<iframe id="webauthn_iframe" sandbox="allow-scripts allow-same-origin"></iframe>
|
||||||
|
</div>
|
||||||
|
<ng-container *ngIf="webAuthnNewTab">
|
||||||
|
<div class="content text-center" *ngIf="webAuthnNewTab">
|
||||||
|
<p class="text-center">{{ "webAuthnNewTab" | i18n }}</p>
|
||||||
|
<button type="button" class="btn primary block" (click)="authWebAuthn()" appStopClick>
|
||||||
|
{{ "webAuthnNewTabOpen" | i18n }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
@ -0,0 +1,131 @@
|
|||||||
|
import { DialogModule } from "@angular/cdk/dialog";
|
||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component, EventEmitter, Inject, Output } from "@angular/core";
|
||||||
|
import { ReactiveFormsModule, FormsModule } from "@angular/forms";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
|
||||||
|
import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
|
||||||
|
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
|
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||||
|
import { WebAuthnIFrame } from "@bitwarden/common/auth/webauthn-iframe";
|
||||||
|
import { ClientType } from "@bitwarden/common/enums";
|
||||||
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import {
|
||||||
|
ButtonModule,
|
||||||
|
LinkModule,
|
||||||
|
TypographyModule,
|
||||||
|
FormFieldModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: "app-two-factor-auth-webauthn",
|
||||||
|
templateUrl: "two-factor-auth-webauthn.component.html",
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
JslibModule,
|
||||||
|
DialogModule,
|
||||||
|
ButtonModule,
|
||||||
|
LinkModule,
|
||||||
|
TypographyModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormFieldModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
FormsModule,
|
||||||
|
],
|
||||||
|
providers: [I18nPipe],
|
||||||
|
})
|
||||||
|
export class TwoFactorAuthWebAuthnComponent {
|
||||||
|
@Output() token = new EventEmitter<string>();
|
||||||
|
|
||||||
|
webAuthnReady = false;
|
||||||
|
webAuthnNewTab = false;
|
||||||
|
webAuthnSupported = false;
|
||||||
|
webAuthn: WebAuthnIFrame = null;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected i18nService: I18nService,
|
||||||
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
|
@Inject(WINDOW) protected win: Window,
|
||||||
|
protected environmentService: EnvironmentService,
|
||||||
|
protected twoFactorService: TwoFactorService,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
) {
|
||||||
|
this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win);
|
||||||
|
|
||||||
|
if (this.platformUtilsService.getClientType() == ClientType.Browser) {
|
||||||
|
// FIXME: Chromium 110 has broken WebAuthn support in extensions via an iframe
|
||||||
|
this.webAuthnNewTab = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
if (this.route.snapshot.paramMap.has("webAuthnResponse")) {
|
||||||
|
this.token.emit(this.route.snapshot.paramMap.get("webAuthnResponse"));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cleanupWebAuthn();
|
||||||
|
|
||||||
|
if (this.win != null && this.webAuthnSupported) {
|
||||||
|
const env = await firstValueFrom(this.environmentService.environment$);
|
||||||
|
const webVaultUrl = env.getWebVaultUrl();
|
||||||
|
this.webAuthn = new WebAuthnIFrame(
|
||||||
|
this.win,
|
||||||
|
webVaultUrl,
|
||||||
|
this.webAuthnNewTab,
|
||||||
|
this.platformUtilsService,
|
||||||
|
this.i18nService,
|
||||||
|
(token: string) => {
|
||||||
|
this.token.emit(token);
|
||||||
|
},
|
||||||
|
(error: string) => {
|
||||||
|
this.platformUtilsService.showToast(
|
||||||
|
"error",
|
||||||
|
this.i18nService.t("errorOccurred"),
|
||||||
|
this.i18nService.t("webauthnCancelOrTimeout"),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(info: string) => {
|
||||||
|
if (info === "ready") {
|
||||||
|
this.webAuthnReady = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this.webAuthnNewTab) {
|
||||||
|
setTimeout(async () => {
|
||||||
|
await this.authWebAuthn();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.cleanupWebAuthn();
|
||||||
|
}
|
||||||
|
|
||||||
|
async authWebAuthn() {
|
||||||
|
const providerData = (await this.twoFactorService.getProviders()).get(
|
||||||
|
TwoFactorProviderType.WebAuthn,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this.webAuthnSupported || this.webAuthn == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.webAuthn.init(providerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private cleanupWebAuthn() {
|
||||||
|
if (this.webAuthn != null) {
|
||||||
|
this.webAuthn.stop();
|
||||||
|
this.webAuthn.cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,10 @@
|
|||||||
(token)="token = $event"
|
(token)="token = $event"
|
||||||
*ngIf="selectedProviderType === providerType.Yubikey"
|
*ngIf="selectedProviderType === providerType.Yubikey"
|
||||||
/>
|
/>
|
||||||
|
<app-two-factor-auth-webauthn
|
||||||
|
(token)="token = $event; submitForm()"
|
||||||
|
*ngIf="selectedProviderType === providerType.WebAuthn"
|
||||||
|
/>
|
||||||
<bit-form-control *ngIf="selectedProviderType != null">
|
<bit-form-control *ngIf="selectedProviderType != null">
|
||||||
<bit-label>{{ "rememberMe" | i18n }}</bit-label>
|
<bit-label>{{ "rememberMe" | i18n }}</bit-label>
|
||||||
<input type="checkbox" bitCheckbox formControlName="remember" />
|
<input type="checkbox" bitCheckbox formControlName="remember" />
|
||||||
@ -31,7 +35,7 @@
|
|||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
bitButton
|
bitButton
|
||||||
bitFormButton
|
bitFormButton
|
||||||
*ngIf="selectedProviderType != null"
|
*ngIf="selectedProviderType != null && selectedProviderType !== providerType.WebAuthn"
|
||||||
>
|
>
|
||||||
<span> <i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ actionButtonText }} </span>
|
<span> <i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ actionButtonText }} </span>
|
||||||
</button>
|
</button>
|
||||||
|
@ -40,6 +40,7 @@ import { CaptchaProtectedComponent } from "../captcha-protected.component";
|
|||||||
|
|
||||||
import { TwoFactorAuthAuthenticatorComponent } from "./two-factor-auth-authenticator.component";
|
import { TwoFactorAuthAuthenticatorComponent } from "./two-factor-auth-authenticator.component";
|
||||||
import { TwoFactorAuthEmailComponent } from "./two-factor-auth-email.component";
|
import { TwoFactorAuthEmailComponent } from "./two-factor-auth-email.component";
|
||||||
|
import { TwoFactorAuthWebAuthnComponent } from "./two-factor-auth-webauthn.component";
|
||||||
import { TwoFactorAuthYubikeyComponent } from "./two-factor-auth-yubikey.component";
|
import { TwoFactorAuthYubikeyComponent } from "./two-factor-auth-yubikey.component";
|
||||||
import {
|
import {
|
||||||
TwoFactorOptionsDialogResult,
|
TwoFactorOptionsDialogResult,
|
||||||
@ -63,6 +64,7 @@ import {
|
|||||||
TwoFactorAuthAuthenticatorComponent,
|
TwoFactorAuthAuthenticatorComponent,
|
||||||
TwoFactorAuthEmailComponent,
|
TwoFactorAuthEmailComponent,
|
||||||
TwoFactorAuthYubikeyComponent,
|
TwoFactorAuthYubikeyComponent,
|
||||||
|
TwoFactorAuthWebAuthnComponent,
|
||||||
],
|
],
|
||||||
providers: [I18nPipe],
|
providers: [I18nPipe],
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user