Bug 1383799 - Cancel WebAuthn operations on tab-switch r=ttaubert

WebAuthn operations that are in-flight with authenticators must be cancelled
when switching tabs.

There's an Issue [1] opened with the WebAuthn spec for this already, but the
language is _not_ in spec. Still, it's necessary for security, spec or not.

This also matches how Chromium handles U2F operations during a tab switch.

[1] https://github.com/w3c/webauthn/issues/316

MozReview-Commit-ID: 6Qh9oC4pqys

--HG--
extra : rebase_source : ad1665b8140f74b1291f17994285e6146c4ec468
This commit is contained in:
J.C. Jones 2017-08-04 12:34:18 -07:00
Родитель fc6de5f2b5
Коммит b491193ac3
2 изменённых файлов: 92 добавлений и 2 удалений

Просмотреть файл

@ -42,7 +42,10 @@ StaticRefPtr<WebAuthnManager> gWebAuthnManager;
static mozilla::LazyLogModule gWebAuthnManagerLog("webauthnmanager");
}
NS_IMPL_ISUPPORTS(WebAuthnManager, nsIIPCBackgroundChildCreateCallback);
NS_NAMED_LITERAL_STRING(kVisibilityChange, "visibilitychange");
NS_IMPL_ISUPPORTS(WebAuthnManager, nsIIPCBackgroundChildCreateCallback,
nsIDOMEventListener);
/***********************************************************************
* Utility Functions
@ -131,6 +134,7 @@ nsresult
GetOrigin(nsPIDOMWindowInner* aParent,
/*out*/ nsAString& aOrigin, /*out*/ nsACString& aHost)
{
MOZ_ASSERT(aParent);
nsCOMPtr<nsIDocument> doc = aParent->GetDoc();
MOZ_ASSERT(doc);
@ -168,6 +172,7 @@ RelaxSameOrigin(nsPIDOMWindowInner* aParent,
MOZ_ASSERT(aParent);
nsCOMPtr<nsIDocument> doc = aParent->GetDoc();
MOZ_ASSERT(doc);
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
nsCOMPtr<nsIURI> uri;
if (NS_FAILED(principal->GetURI(getter_AddRefs(uri)))) {
@ -212,6 +217,41 @@ RelaxSameOrigin(nsPIDOMWindowInner* aParent,
return NS_OK;
}
static void
ListenForVisibilityEvents(nsPIDOMWindowInner* aParent,
WebAuthnManager* aListener)
{
MOZ_ASSERT(aParent);
MOZ_ASSERT(aListener);
nsCOMPtr<nsIDocument> doc = aParent->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
return;
}
nsresult rv = doc->AddSystemEventListener(kVisibilityChange, aListener,
/* use capture */ true,
/* wants untrusted */ false);
Unused << NS_WARN_IF(NS_FAILED(rv));
}
static void
StopListeningForVisibilityEvents(nsPIDOMWindowInner* aParent,
WebAuthnManager* aListener)
{
MOZ_ASSERT(aParent);
MOZ_ASSERT(aListener);
nsCOMPtr<nsIDocument> doc = aParent->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
return;
}
nsresult rv = doc->RemoveSystemEventListener(kVisibilityChange, aListener,
/* use capture */ true);
Unused << NS_WARN_IF(NS_FAILED(rv));
}
/***********************************************************************
* WebAuthnManager Implementation
**********************************************************************/
@ -227,6 +267,11 @@ WebAuthnManager::MaybeClearTransaction()
mClientData.reset();
mInfo.reset();
mTransactionPromise = nullptr;
if (mCurrentParent) {
StopListeningForVisibilityEvents(mCurrentParent, this);
mCurrentParent = nullptr;
}
if (mChild) {
RefPtr<WebAuthnTransactionChild> c;
mChild.swap(c);
@ -503,6 +548,8 @@ WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent,
mClientData = Some(clientDataJSON);
mCurrentParent = aParent;
mInfo = Some(info);
ListenForVisibilityEvents(aParent, this);
return promise.forget();
}
@ -520,6 +567,13 @@ WebAuthnManager::StartSign() {
}
}
void
WebAuthnManager::StartCancel() {
if (mChild) {
mChild->SendRequestCancel();
}
}
already_AddRefed<Promise>
WebAuthnManager::GetAssertion(nsPIDOMWindowInner* aParent,
const PublicKeyCredentialRequestOptions& aOptions)
@ -669,6 +723,8 @@ WebAuthnManager::GetAssertion(nsPIDOMWindowInner* aParent,
mClientData = Some(clientDataJSON);
mCurrentParent = aParent;
mInfo = Some(info);
ListenForVisibilityEvents(aParent, this);
return promise.forget();
}
@ -877,12 +933,41 @@ WebAuthnManager::FinishGetAssertion(nsTArray<uint8_t>& aCredentialId,
void
WebAuthnManager::Cancel(const nsresult& aError)
{
MOZ_ASSERT(NS_IsMainThread());
if (mTransactionPromise) {
mTransactionPromise->MaybeReject(aError);
}
MaybeClearTransaction();
}
NS_IMETHODIMP
WebAuthnManager::HandleEvent(nsIDOMEvent* aEvent)
{
MOZ_ASSERT(aEvent);
nsAutoString type;
aEvent->GetType(type);
if (!type.Equals(kVisibilityChange)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
MOZ_ASSERT(doc);
if (doc && doc->Hidden()) {
MOZ_LOG(gWebAuthnManagerLog, LogLevel::Debug,
("Visibility change: WebAuthn window is hidden, cancelling job."));
StartCancel();
Cancel(NS_ERROR_ABORT);
}
return NS_OK;
}
void
WebAuthnManager::ActorCreated(PBackgroundChild* aActor)
{

Просмотреть файл

@ -8,7 +8,9 @@
#define mozilla_dom_WebAuthnManager_h
#include "mozilla/MozPromise.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/PWebAuthnTransaction.h"
#include "nsIDOMEventListener.h"
#include "nsIIPCBackgroundChildCreateCallback.h"
/*
@ -61,10 +63,12 @@ class Promise;
class WebAuthnTransactionChild;
class WebAuthnTransactionInfo;
class WebAuthnManager final : public nsIIPCBackgroundChildCreateCallback
class WebAuthnManager final : public nsIIPCBackgroundChildCreateCallback,
public nsIDOMEventListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
static WebAuthnManager* GetOrCreate();
static WebAuthnManager* Get();
@ -88,6 +92,7 @@ public:
void StartRegister();
void StartSign();
void StartCancel();
// nsIIPCbackgroundChildCreateCallback methods
void ActorCreated(PBackgroundChild* aActor) override;