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

Add support for WebAuthn to browser extension (#1379)

This commit is contained in:
Oscar Hinton
2021-03-17 22:14:26 +01:00
committed by GitHub
parent 24d172e3b9
commit e0f4386042
13 changed files with 97 additions and 55 deletions

View File

@@ -9,7 +9,7 @@
<div class="right">
<button type="submit" appBlurClick [disabled]="form.loading" *ngIf="selectedProviderType != null && selectedProviderType !== providerType.Duo &&
selectedProviderType !== providerType.OrganizationDuo &&
(selectedProviderType !== providerType.U2f || form.loading)">
(selectedProviderType !== providerType.WebAuthn || form.loading)">
<span [hidden]="form.loading">{{'continue' | i18n}}</span>
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button>
@@ -59,15 +59,9 @@
</div>
</div>
</ng-container>
<ng-container *ngIf="selectedProviderType === providerType.U2f">
<div class="content text-center">
<span *ngIf="!u2fReady" class="text-center"><i class="fa fa-spinner fa-spin"></i></span>
<div *ngIf="u2fReady">
<p>{{'insertU2f' | i18n}}</p>
<img src="../images/u2fkey.jpg" alt="" class="img-rounded img-responsive" />
</div>
</div>
<div class="box first">
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn && !webAuthnNewTab">
<div id="web-authn-frame"><iframe id="webauthn_iframe"></iframe></div>
<div class="box">
<div class="box-content">
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="remember">{{'rememberMe' | i18n}}</label>
@@ -76,6 +70,11 @@
</div>
</div>
</ng-container>
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn && webAuthnNewTab">
<div class="content text-center" *ngIf="webAuthnNewTab">
<p class="text-center">{{'webAuthnNewTab' | i18n}}</p>
</div>
</ng-container>
<ng-container *ngIf="selectedProviderType === providerType.Duo ||
selectedProviderType === providerType.OrganizationDuo">
<div id="duo-frame"><iframe id="duo_iframe"></iframe></div>
@@ -104,4 +103,3 @@
</div>
</content>
</form>
<iframe id="u2f_iframe" hidden></iframe>

View File

@@ -15,6 +15,7 @@ import { ApiService } from 'jslib/abstractions/api.service';
import { AuthService } from 'jslib/abstractions/auth.service';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { StateService } from 'jslib/abstractions/state.service';
import { StorageService } from 'jslib/abstractions/storage.service';
@@ -43,22 +44,31 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
environmentService: EnvironmentService, private ngZone: NgZone,
private broadcasterService: BroadcasterService, private changeDetectorRef: ChangeDetectorRef,
private popupUtilsService: PopupUtilsService, stateService: StateService,
storageService: StorageService, route: ActivatedRoute) {
storageService: StorageService, route: ActivatedRoute, private messagingService: MessagingService) {
super(authService, router, i18nService, apiService, platformUtilsService, window, environmentService,
stateService, storageService, route);
super.onSuccessfulLogin = () => {
return syncService.fullSync(true);
};
super.successRoute = '/tabs/vault';
this.webAuthnNewTab = this.platformUtilsService.isFirefox() || this.platformUtilsService.isSafari();
}
async ngOnInit() {
const isFirefox = this.platformUtilsService.isFirefox();
if (this.popupUtilsService.inPopup(window) && isFirefox &&
this.win.navigator.userAgent.indexOf('Windows NT 10.0;') > -1) {
// ref: https://bugzilla.mozilla.org/show_bug.cgi?id=1562620
this.initU2f = false;
if (this.route.snapshot.paramMap.has('webAuthnResponse')) {
// WebAuthn fallback response
this.selectedProviderType = TwoFactorProviderType.WebAuthn;
this.token = this.route.snapshot.paramMap.get('webAuthnResponse');
super.onSuccessfulLogin = async () => {
this.syncService.fullSync(true);
this.messagingService.send('reloadPopup');
window.close();
};
this.remember = this.route.snapshot.paramMap.get('remember') === 'true';
await this.doSubmit();
return;
}
await super.ngOnInit();
if (this.selectedProviderType == null) {
return;
@@ -73,15 +83,6 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
}
}
if (!this.initU2f && this.selectedProviderType === TwoFactorProviderType.U2f &&
this.popupUtilsService.inPopup(window)) {
const confirmed = await this.platformUtilsService.showDialog(this.i18nService.t('popupU2fCloseMessage'),
null, this.i18nService.t('yes'), this.i18nService.t('no'));
if (confirmed) {
this.popupUtilsService.popOut(window);
}
}
const queryParamsSub = this.route.queryParams.subscribe(async qParams => {
if (qParams.sso === 'true') {
super.onSuccessfulLogin = () => {

View File

@@ -176,6 +176,19 @@ p.lead {
}
}
#web-authn-frame {
background: url('../images/loading.svg') 0 0 no-repeat;
width: 100%;
height: 310px;
margin-bottom: -10px;
iframe {
width: 100%;
height: 100%;
border: none;
}
}
app-root > #loading {
display: flex;
text-align: center;

View File

@@ -66,11 +66,6 @@ function getBgService<T>(service: string) {
export const stateService = new StateService();
export const messagingService = new BrowserMessagingService();
export const authService = new AuthService(getBgService<CryptoService>('cryptoService')(),
getBgService<ApiService>('apiService')(), getBgService<UserService>('userService')(),
getBgService<TokenService>('tokenService')(), getBgService<AppIdService>('appIdService')(),
getBgService<I18nService>('i18nService')(), getBgService<PlatformUtilsService>('platformUtilsService')(),
messagingService, getBgService<VaultTimeoutService>('vaultTimeoutService')(), getBgService<ConsoleLogService>('consoleLogService')());
export const searchService = new PopupSearchService(getBgService<SearchService>('searchService')(),
getBgService<CipherService>('cipherService')(), getBgService<ConsoleLogService>('consoleLogService')());
@@ -86,7 +81,7 @@ export function initFactory(platformUtilsService: PlatformUtilsService, i18nServ
window.document.body.classList.add('body-sm');
}
document.body.style.setProperty('height',`${window.innerHeight}px`,'important');
document.body.style.setProperty('height', `${window.innerHeight}px`, 'important');
}
if (BrowserApi.getBackgroundPage() != null) {
@@ -108,8 +103,6 @@ export function initFactory(platformUtilsService: PlatformUtilsService, i18nServ
window.document.documentElement.classList.add('locale_' + i18nService.translationLocale);
window.document.documentElement.classList.add('theme_' + theme);
authService.init();
const analytics = new Analytics(window, () => BrowserApi.gaFilter(), null, null, null, () => {
const bgPage = BrowserApi.getBackgroundPage();
if (bgPage == null || bgPage.bitwardenMain == null) {
@@ -133,7 +126,7 @@ export function initFactory(platformUtilsService: PlatformUtilsService, i18nServ
PopupUtilsService,
BroadcasterService,
{ provide: MessagingService, useValue: messagingService },
{ provide: AuthServiceAbstraction, useValue: authService },
{ provide: AuthServiceAbstraction, useFactory: getBgService<AuthService>('authService'), deps: [] },
{ provide: StateServiceAbstraction, useValue: stateService },
{ provide: SearchServiceAbstraction, useValue: searchService },
{ provide: AuditService, useFactory: getBgService<AuditService>('auditService'), deps: [] },