diff --git a/jslib b/jslib index f80e89465f..f20af0cd7c 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit f80e89465ffc004705d2941301c0ffb6bfd71d1a +Subproject commit f20af0cd7c90adc07783950bed197b5d47892d6f diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index aca1797240..5c18dfebd0 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -798,6 +798,12 @@ "insertU2f": { "message": "Insert your security key into your computer's USB port. If it has a button, touch it." }, + "webAuthnNewTab": { + "message": "Continue the WebAuthn 2FA verification in the new tab." + }, + "webAuthnAuthenticate": { + "message": "Authenticate WebAutn" + }, "loginUnavailable": { "message": "Login Unavailable" }, @@ -837,11 +843,11 @@ "message": "Verify with Duo Security for your organization using the Duo Mobile app, SMS, phone call, or U2F security key.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, - "u2fDesc": { - "message": "Use any FIDO U2F enabled security key to access your account." + "webAuthnTitle": { + "message": "FIDO2 WebAuthn" }, - "u2fTitle": { - "message": "FIDO U2F Security Key" + "webAuthnDesc": { + "message": "Use any WebAuthn enabled security key to access your account." }, "emailTitle": { "message": "Email" diff --git a/src/background/main.background.ts b/src/background/main.background.ts index 7d251d7fad..382c4ba16c 100644 --- a/src/background/main.background.ts +++ b/src/background/main.background.ts @@ -179,9 +179,6 @@ export default class MainBackground { this.apiService = new ApiService(this.tokenService, this.platformUtilsService, (expired: boolean) => this.logout(expired)); this.userService = new UserService(this.tokenService, this.storageService); - this.authService = new AuthService(this.cryptoService, this.apiService, this.userService, - this.tokenService, this.appIdService, this.i18nService, this.platformUtilsService, - this.messagingService, this.vaultTimeoutService, this.consoleLogService); this.settingsService = new SettingsService(this.userService, this.storageService); this.cipherService = new CipherService(this.cryptoService, this.userService, this.settingsService, this.apiService, this.storageService, this.i18nService, () => this.searchService); @@ -246,7 +243,7 @@ export default class MainBackground { this.runtimeBackground = new RuntimeBackground(this, this.autofillService, this.cipherService, this.platformUtilsService as BrowserPlatformUtilsService, this.storageService, this.i18nService, this.analytics, this.notificationsService, this.systemService, this.vaultTimeoutService, - this.environmentService, this.policyService, this.userService); + this.environmentService, this.policyService, this.userService, this.messagingService); this.nativeMessagingBackground = new NativeMessagingBackground(this.storageService, this.cryptoService, this.cryptoFunctionService, this.vaultTimeoutService, this.runtimeBackground, this.i18nService, this.userService, this.messagingService, this.appIdService); this.commandsBackground = new CommandsBackground(this, this.passwordGenerationService, @@ -261,12 +258,24 @@ export default class MainBackground { this.webRequestBackground = new WebRequestBackground(this.platformUtilsService, this.cipherService, this.vaultTimeoutService); this.windowsBackground = new WindowsBackground(this); + + const that = this; + this.authService = new AuthService(this.cryptoService, this.apiService, this.userService, + this.tokenService, this.appIdService, this.i18nService, this.platformUtilsService, + new class extends MessagingServiceAbstraction { + // AuthService should send the messages to the background not popup. + send = (subscriber: string, arg: any = {}) => { + const message = Object.assign({}, { command: subscriber }, arg); + that.runtimeBackground.processMessage(message, that, null); + } + }(), this.vaultTimeoutService, this.consoleLogService); } async bootstrap() { this.analytics.ga('send', 'pageview', '/background.html'); this.containerService.attachToWindow(window); + (this.authService as AuthService).init(); await (this.vaultTimeoutService as VaultTimeoutService).init(true); await (this.i18nService as I18nService).init(); await (this.eventService as EventService).init(true); diff --git a/src/background/runtime.background.ts b/src/background/runtime.background.ts index 916c55e8cc..1bfdbd8b85 100644 --- a/src/background/runtime.background.ts +++ b/src/background/runtime.background.ts @@ -7,6 +7,7 @@ import { LoginView } from 'jslib/models/view/loginView'; import { CipherService } from 'jslib/abstractions/cipher.service'; import { EnvironmentService } from 'jslib/abstractions/environment.service'; import { I18nService } from 'jslib/abstractions/i18n.service'; +import { MessagingService } from 'jslib/abstractions/messaging.service'; import { NotificationsService } from 'jslib/abstractions/notifications.service'; import { PolicyService } from 'jslib/abstractions/policy.service'; import { StorageService } from 'jslib/abstractions/storage.service'; @@ -39,7 +40,7 @@ export default class RuntimeBackground { private analytics: Analytics, private notificationsService: NotificationsService, private systemService: SystemService, private vaultTimeoutService: VaultTimeoutService, private environmentService: EnvironmentService, private policyService: PolicyService, - private userService: UserService) { + private userService: UserService, private messagingService: MessagingService) { // onInstalled listener must be wired up before anything else, so we do it in the ctor chrome.runtime.onInstalled.addListener((details: any) => { @@ -176,6 +177,22 @@ export default class RuntimeBackground { } catch { } break; + case 'webAuthnResult': + let vaultUrl2 = this.environmentService.getWebVaultUrl(); + if (vaultUrl2 == null) { + vaultUrl2 = 'https://vault.bitwarden.com'; + } + + if (msg.referrer == null || Utils.getHostname(vaultUrl2) !== msg.referrer) { + return; + } + + const params = `webAuthnResponse=${encodeURIComponent(msg.data)};remember=${msg.remember}`; + BrowserApi.createNewTab(`popup/index.html?uilocation=popout#/2fa;${params}`, undefined, false); + break; + case 'reloadPopup': + this.messagingService.send('reloadPopup'); + break; default: break; } diff --git a/src/browser/browserApi.ts b/src/browser/browserApi.ts index 0993d79799..8f23c0911c 100644 --- a/src/browser/browserApi.ts +++ b/src/browser/browserApi.ts @@ -87,8 +87,8 @@ export class BrowserApi { return Promise.resolve(chrome.extension.getViews({ type: 'popup' }).length > 0); } - static createNewTab(url: string, extensionPage: boolean = false) { - chrome.tabs.create({ url: url }); + static createNewTab(url: string, extensionPage: boolean = false, active: boolean = true) { + chrome.tabs.create({ url: url, active: active }); } static messageListener(name: string, callback: (message: any, sender: any, response: any) => void) { diff --git a/src/content/sso.ts b/src/content/message_handler.ts similarity index 56% rename from src/content/sso.ts rename to src/content/message_handler.ts index 8c57d72de3..4358a97945 100644 --- a/src/content/sso.ts +++ b/src/content/message_handler.ts @@ -10,4 +10,13 @@ window.addEventListener('message', event => { referrer: event.source.location.hostname, }); } + + if (event.data.command && (event.data.command === 'webAuthnResult')) { + chrome.runtime.sendMessage({ + command: event.data.command, + data: event.data.data, + remember: event.data.remember, + referrer: event.source.location.hostname, + }); + } }, false); diff --git a/src/manifest.json b/src/manifest.json index 213fba3d03..e82913d5d5 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -44,7 +44,7 @@ { "all_frames": false, "js": [ - "content/sso.js" + "content/message_handler.js" ], "matches": [ "http://*/*", diff --git a/src/popup/accounts/two-factor.component.html b/src/popup/accounts/two-factor.component.html index 5b2ee5e333..db6518b817 100644 --- a/src/popup/accounts/two-factor.component.html +++ b/src/popup/accounts/two-factor.component.html @@ -9,7 +9,7 @@
{{'insertU2f' | i18n}}
- -{{'webAuthnNewTab' | i18n}}
+