Bug 769283 - Add a separate HTTP auth cache for private channels. r=mayhemer
authorJosh Matthews <josh@joshmatthews.net>
Mon, 29 Oct 2012 16:20:03 -0400
changeset 111828 ce8e092cb0790b2175bbbca04bf5f55bf64675b6
parent 111827 4ee6b1acd5ea36e402f3b5ebe23a755275e51366
child 111829 53b97b4ec554925108480bb34ecf5cdeb0659a62
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersmayhemer
bugs769283
milestone19.0a1
Bug 769283 - Add a separate HTTP auth cache for private channels. r=mayhemer
browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
browser/components/privatebrowsing/test/unit/test_httpauth.js
dom/plugins/base/nsNPAPIPlugin.cpp
mobile/android/chrome/content/sanitize.js
netwerk/protocol/http/nsHttpAuthManager.cpp
netwerk/protocol/http/nsHttpAuthManager.h
netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
netwerk/protocol/http/nsHttpChannelAuthProvider.h
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpHandler.h
netwerk/protocol/http/nsIHttpAuthManager.idl
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -436,21 +436,16 @@ PrivateBrowsingService.prototype = {
         this._unload();
         break;
       case "private-browsing":
         // clear all auth tokens
         let sdr = Cc["@mozilla.org/security/sdr;1"].
                   getService(Ci.nsISecretDecoderRing);
         sdr.logoutAndTeardown();
     
-        // clear plain HTTP auth sessions
-        let authMgr = Cc['@mozilla.org/network/http-auth-manager;1'].
-                      getService(Ci.nsIHttpAuthManager);
-        authMgr.clearAll();
-
         try {
           this._prefs.deleteBranch("geo.wifi.access_token.");
         } catch (ex) {}
 
         if (!this._inPrivateBrowsing) {
           // Clear the error console
           let consoleService = Cc["@mozilla.org/consoleservice;1"].
                                getService(Ci.nsIConsoleService);
--- a/browser/components/privatebrowsing/test/unit/test_httpauth.js
+++ b/browser/components/privatebrowsing/test/unit/test_httpauth.js
@@ -1,79 +1,102 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This test makes sure the HTTP authenticated sessions are correctly cleared
 // when entering and leaving the private browsing mode.
 
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 function run_test_on_service() {
-  var pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
-           getService(Ci.nsIPrivateBrowsingService);
-
   var am = Cc["@mozilla.org/network/http-auth-manager;1"].
            getService(Ci.nsIHttpAuthManager);
 
   const kHost1 = "pbtest3.example.com";
   const kHost2 = "pbtest4.example.com";
   const kPort = 80;
   const kHTTP = "http";
   const kBasic = "basic";
   const kRealm = "realm";
   const kDomain = "example.com";
   const kUser = "user";
   const kUser2 = "user2";
   const kPassword = "pass";
   const kPassword2 = "pass2";
   const kEmpty = "";
-
+  
+  const PRIVATE = true;
+  const NOT_PRIVATE = false;
+  
   try {
     var domain = {value: kEmpty}, user = {value: kEmpty}, pass = {value: kEmpty};
     // simulate a login via HTTP auth outside of the private mode
     am.setAuthIdentity(kHTTP, kHost1, kPort, kBasic, kRealm, kEmpty, kDomain, kUser, kPassword);
     // make sure the recently added auth entry is available outside the private browsing mode
-    am.getAuthIdentity(kHTTP, kHost1, kPort, kBasic, kRealm, kEmpty, domain, user, pass);
+    am.getAuthIdentity(kHTTP, kHost1, kPort, kBasic, kRealm, kEmpty, domain, user, pass, NOT_PRIVATE);
     do_check_eq(domain.value, kDomain);
     do_check_eq(user.value, kUser);
     do_check_eq(pass.value, kPassword);
-    // enter private browsing mode
-    pb.privateBrowsingEnabled = true;
-    // make sure the added auth entry is no longer accessible
+
+    // make sure the added auth entry is no longer accessible in private
     domain = {value: kEmpty}, user = {value: kEmpty}, pass = {value: kEmpty};
     try {
       // should throw
-      am.getAuthIdentity(kHTTP, kHost1, kPort, kBasic, kRealm, kEmpty, domain, user, pass);
+      am.getAuthIdentity(kHTTP, kHost1, kPort, kBasic, kRealm, kEmpty, domain, user, pass, PRIVATE);
       do_throw("Auth entry should not be retrievable after entering the private browsing mode");
     } catch (e) {
       do_check_eq(domain.value, kEmpty);
       do_check_eq(user.value, kEmpty);
       do_check_eq(pass.value, kEmpty);
     }
 
     // simulate a login via HTTP auth inside of the private mode
-    am.setAuthIdentity(kHTTP, kHost2, kPort, kBasic, kRealm, kEmpty, kDomain, kUser2, kPassword2);
-    // make sure the recently added auth entry is available outside the private browsing mode
+    am.setAuthIdentity(kHTTP, kHost2, kPort, kBasic, kRealm, kEmpty, kDomain, kUser2, kPassword2, PRIVATE);
+    // make sure the recently added auth entry is available inside the private browsing mode
     domain = {value: kEmpty}, user = {value: kEmpty}, pass = {value: kEmpty};
-    am.getAuthIdentity(kHTTP, kHost2, kPort, kBasic, kRealm, kEmpty, domain, user, pass);
+    am.getAuthIdentity(kHTTP, kHost2, kPort, kBasic, kRealm, kEmpty, domain, user, pass, PRIVATE);
     do_check_eq(domain.value, kDomain);
     do_check_eq(user.value, kUser2);
     do_check_eq(pass.value, kPassword2);
-    // exit private browsing mode
-    pb.privateBrowsingEnabled = false;
-    // make sure the added auth entry is no longer accessible
+
+    try {
+      // make sure the recently added auth entry is not available outside the private browsing mode
+      domain = {value: kEmpty}, user = {value: kEmpty}, pass = {value: kEmpty};
+      am.getAuthIdentity(kHTTP, kHost2, kPort, kBasic, kRealm, kEmpty, domain, user, pass, NOT_PRIVATE);
+      do_throw("Auth entry should not be retrievable outside of private browsing mode");
+    } catch (x) {
+      do_check_eq(domain.value, kEmpty);
+      do_check_eq(user.value, kEmpty);
+      do_check_eq(pass.value, kEmpty);
+    }
+
+    // simulate leaving private browsing mode
+    Services.obs.notifyObservers(null, "last-pb-context-exited", null);
+
+    // make sure the added auth entry is no longer accessible in any privacy state
     domain = {value: kEmpty}, user = {value: kEmpty}, pass = {value: kEmpty};
     try {
-      // should throw
-      am.getAuthIdentity(kHTTP, kHost2, kPort, kBasic, kRealm, kEmpty, domain, user, pass);
+      // should throw (not available in public mode)
+      am.getAuthIdentity(kHTTP, kHost2, kPort, kBasic, kRealm, kEmpty, domain, user, pass, NOT_PRIVATE);
       do_throw("Auth entry should not be retrievable after exiting the private browsing mode");
     } catch (e) {
       do_check_eq(domain.value, kEmpty);
       do_check_eq(user.value, kEmpty);
       do_check_eq(pass.value, kEmpty);
     }
+    try {
+      // should throw (no longer available in private mode)
+      am.getAuthIdentity(kHTTP, kHost2, kPort, kBasic, kRealm, kEmpty, domain, user, pass, PRIVATE);
+      do_throw("Auth entry should not be retrievable in private mode after exiting the private browsing mode");
+    } catch (x) {
+      do_check_eq(domain.value, kEmpty);
+      do_check_eq(user.value, kEmpty);
+      do_check_eq(pass.value, kEmpty);
+    }
   } catch (e) {
     do_throw("Unexpected exception while testing HTTP auth manager: " + e);
   }
 }
 
 // Support running tests on both the service itself and its wrapper
 function run_test() {
   run_test_on_all_services();
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -650,16 +650,30 @@ static JSContext *
 GetJSContextFromNPP(NPP npp)
 {
   nsIDocument *doc = GetDocumentFromNPP(npp);
   NS_ENSURE_TRUE(doc, nullptr);
 
   return GetJSContextFromDoc(doc);
 }
 
+static nsresult
+GetPrivacyFromNPP(NPP npp, bool* aPrivate)
+{
+  nsCOMPtr<nsIDocument> doc = GetDocumentFromNPP(npp);
+  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+  nsCOMPtr<nsPIDOMWindow> domwindow = doc->GetWindow();
+  NS_ENSURE_TRUE(domwindow, NS_ERROR_FAILURE);
+
+  nsCOMPtr<nsIDocShell> docShell = domwindow->GetDocShell();
+  nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
+  *aPrivate = loadContext && loadContext->UsePrivateBrowsing();
+  return NS_OK;
+}
+
 static NPIdentifier
 doGetIdentifier(JSContext *cx, const NPUTF8* name)
 {
   NS_ConvertUTF8toUTF16 utf16name(name);
 
   JSString *str = ::JS_InternUCStringN(cx, (jschar *)utf16name.get(),
                                        utf16name.Length());
 
@@ -2025,26 +2039,22 @@ NPError NP_CALLBACK
     *(NPBool*)result = true;
 #else
     *(NPBool*)result = false;
 #endif
     return NPERR_NO_ERROR;
   }
 
   case NPNVprivateModeBool: {
-    nsCOMPtr<nsIDocument> doc = GetDocumentFromNPP(npp);
-    NS_ENSURE_TRUE(doc, NPERR_GENERIC_ERROR);
-    nsCOMPtr<nsPIDOMWindow> domwindow = doc->GetWindow();
-    if (domwindow) {
-      nsCOMPtr<nsIDocShell> docShell = domwindow->GetDocShell();
-      nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
-      *(NPBool*)result = (NPBool)(loadContext && loadContext->UsePrivateBrowsing());
-      return NPERR_NO_ERROR;
-    }
-    return NPERR_GENERIC_ERROR;
+    bool privacy;
+    nsresult rv = GetPrivacyFromNPP(npp, &privacy);
+    if (NS_FAILED(rv))
+      return NPERR_GENERIC_ERROR;
+    *(NPBool*)result = (NPBool)privacy;
+    return NPERR_NO_ERROR;
   }
 
   case NPNVdocumentOrigin: {
     nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)npp->ndata;
     if (!inst) {
       return NPERR_GENERIC_ERROR;
     }
 
@@ -2724,22 +2734,25 @@ NPError NP_CALLBACK
       !proto.LowerCaseEqualsLiteral("https"))
     return NPERR_GENERIC_ERROR;
 
   nsCOMPtr<nsIHttpAuthManager> authManager =
     do_GetService("@mozilla.org/network/http-auth-manager;1");
   if (!authManager)
     return NPERR_GENERIC_ERROR;
 
+  bool authPrivate = false;
+  GetPrivacyFromNPP(instance, &authPrivate);
+
   nsAutoString unused, uname16, pwd16;
   if (NS_FAILED(authManager->GetAuthIdentity(proto, nsDependentCString(host),
                                              port, nsDependentCString(scheme),
                                              nsDependentCString(realm),
                                              EmptyCString(), unused, uname16,
-                                             pwd16))) {
+                                             pwd16, authPrivate))) {
     return NPERR_GENERIC_ERROR;
   }
 
   NS_ConvertUTF16toUTF8 uname8(uname16);
   NS_ConvertUTF16toUTF8 pwd8(pwd16);
 
   *username = ToNewCString(uname8);
   *ulen = *username ? uname8.Length() : 0;
--- a/mobile/android/chrome/content/sanitize.js
+++ b/mobile/android/chrome/content/sanitize.js
@@ -210,19 +210,20 @@ Sanitizer.prototype = {
 
     sessions: {
       clear: function ()
       {
         // clear all auth tokens
         var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
         sdr.logoutAndTeardown();
 
-        // clear plain HTTP auth sessions
-        var authMgr = Cc['@mozilla.org/network/http-auth-manager;1'].getService(Ci.nsIHttpAuthManager);
-        authMgr.clearAll();
+        // clear FTP and plain HTTP auth sessions
+        var os = Components.classes["@mozilla.org/observer-service;1"]
+                           .getService(Components.interfaces.nsIObserverService);
+        os.notifyObservers(null, "net:clear-active-logins", null);
       },
 
       get canClear()
       {
         return true;
       }
     }
   }
--- a/netwerk/protocol/http/nsHttpAuthManager.cpp
+++ b/netwerk/protocol/http/nsHttpAuthManager.cpp
@@ -30,46 +30,50 @@ nsresult nsHttpAuthManager::Init()
     rv = ios->GetProtocolHandler("http", getter_AddRefs(handler));
     if (NS_FAILED(rv))
       return rv;
 
     // maybe someone is overriding our HTTP handler implementation?
     NS_ENSURE_TRUE(gHttpHandler, NS_ERROR_UNEXPECTED);
   }
 	
-  mAuthCache = gHttpHandler->AuthCache();
+  mAuthCache = gHttpHandler->AuthCache(false);
+  mPrivateAuthCache = gHttpHandler->AuthCache(true);
   NS_ENSURE_TRUE(mAuthCache, NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(mPrivateAuthCache, NS_ERROR_FAILURE);
   return NS_OK;
 }
 
 nsHttpAuthManager::~nsHttpAuthManager()
 {
 }
 
 NS_IMETHODIMP
 nsHttpAuthManager::GetAuthIdentity(const nsACString & aScheme,
                                    const nsACString & aHost,
                                    int32_t aPort,
                                    const nsACString & aAuthType,
                                    const nsACString & aRealm,
                                    const nsACString & aPath,
                                    nsAString & aUserDomain,
                                    nsAString & aUserName,
-                                   nsAString & aUserPassword)
+                                   nsAString & aUserPassword,
+                                   bool aIsPrivate)
 {
+  nsHttpAuthCache* auth_cache = aIsPrivate ? mPrivateAuthCache : mAuthCache;
   nsHttpAuthEntry * entry = nullptr;
   nsresult rv;
   if (!aPath.IsEmpty())
-    rv = mAuthCache->GetAuthEntryForPath(PromiseFlatCString(aScheme).get(),
+    rv = auth_cache->GetAuthEntryForPath(PromiseFlatCString(aScheme).get(),
                                          PromiseFlatCString(aHost).get(),
                                          aPort,
                                          PromiseFlatCString(aPath).get(),
                                          &entry);
   else
-    rv = mAuthCache->GetAuthEntryForDomain(PromiseFlatCString(aScheme).get(),
+    rv = auth_cache->GetAuthEntryForDomain(PromiseFlatCString(aScheme).get(),
                                            PromiseFlatCString(aHost).get(),
                                            aPort,
                                            PromiseFlatCString(aRealm).get(),
                                            &entry);
 
   if (NS_FAILED(rv))
     return rv;
   if (!entry)
@@ -85,30 +89,38 @@ NS_IMETHODIMP
 nsHttpAuthManager::SetAuthIdentity(const nsACString & aScheme,
                                    const nsACString & aHost,
                                    int32_t aPort,
                                    const nsACString & aAuthType,
                                    const nsACString & aRealm,
                                    const nsACString & aPath,
                                    const nsAString & aUserDomain,
                                    const nsAString & aUserName,
-                                   const nsAString & aUserPassword)
+                                   const nsAString & aUserPassword,
+                                   bool aIsPrivate)
 {
   nsHttpAuthIdentity ident(PromiseFlatString(aUserDomain).get(),
                            PromiseFlatString(aUserName).get(),
                            PromiseFlatString(aUserPassword).get());
 
-  return mAuthCache->SetAuthEntry(PromiseFlatCString(aScheme).get(),
+  nsHttpAuthCache* auth_cache = aIsPrivate ? mPrivateAuthCache : mAuthCache;
+  return auth_cache->SetAuthEntry(PromiseFlatCString(aScheme).get(),
                                   PromiseFlatCString(aHost).get(),
                                   aPort,
                                   PromiseFlatCString(aPath).get(),
                                   PromiseFlatCString(aRealm).get(),
                                   nullptr,  // credentials
                                   nullptr,  // challenge
                                   &ident,
                                   nullptr); // metadata
 }
 
 NS_IMETHODIMP
 nsHttpAuthManager::ClearAll()
 {
-  return mAuthCache->ClearAll();
+  nsresult rv = mAuthCache->ClearAll();
+  nsresult rv2 = mPrivateAuthCache->ClearAll();
+  if (NS_FAILED(rv))
+    return rv;
+  if (NS_FAILED(rv2))
+    return rv2;
+  return NS_OK;
 }
--- a/netwerk/protocol/http/nsHttpAuthManager.h
+++ b/netwerk/protocol/http/nsHttpAuthManager.h
@@ -16,11 +16,12 @@ public:
   NS_DECL_NSIHTTPAUTHMANAGER
 
   nsHttpAuthManager();
   virtual ~nsHttpAuthManager();
   nsresult Init();
 
 protected:
   nsHttpAuthCache *mAuthCache;
+  nsHttpAuthCache *mPrivateAuthCache;
 };
 
 #endif // nsHttpAuthManager_h__
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
@@ -18,16 +18,17 @@
 #include "nsIPrompt.h"
 #include "nsIAuthModule.h"
 #include "nsIDNSService.h"
 #include "nsNetCID.h"
 #include "nsIDNSRecord.h"
 
 nsHttpChannelAuthProvider::nsHttpChannelAuthProvider()
     : mAuthChannel(nullptr)
+    , mIsPrivate(false)
     , mProxyAuthContinuationState(nullptr)
     , mAuthContinuationState(nullptr)
     , mProxyAuth(false)
     , mTriedProxyAuth(false)
     , mTriedHostAuth(false)
     , mSuppressDefensiveAuth(false)
     , mResolvedHost(0)
 {
@@ -63,16 +64,19 @@ nsHttpChannelAuthProvider::Init(nsIHttpA
 
     // reject the URL if it doesn't specify a host
     if (mHost.IsEmpty())
         return NS_ERROR_MALFORMED_URI;
 
     rv = mURI->GetPort(&mPort);
     if (NS_FAILED(rv)) return rv;
 
+    nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(channel);
+    mIsPrivate = NS_UsePrivateBrowsing(bareChannel);
+
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannelAuthProvider::ProcessAuthentication(uint32_t httpStatus,
                                                  bool     SSLConnectFailed)
 {
     LOG(("nsHttpChannelAuthProvider::ProcessAuthentication "
@@ -155,17 +159,17 @@ nsHttpChannelAuthProvider::AddAuthorizat
         if (!mProxyInfo) return NS_ERROR_NO_INTERFACE;
     }
 
     uint32_t loadFlags;
     rv = mAuthChannel->GetLoadFlags(&loadFlags);
     if (NS_FAILED(rv)) return rv;
 
     // this getter never fails
-    nsHttpAuthCache *authCache = gHttpHandler->AuthCache();
+    nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
 
     // check if proxy credentials should be sent
     const char *proxyHost = ProxyHost();
     if (proxyHost && UsingHttpProxy())
         SetAuthorizationHeader(authCache, nsHttp::Proxy_Authorization,
                                "http", proxyHost, ProxyPort(),
                                nullptr, // proxy has no path
                                mProxyIdent);
@@ -361,17 +365,17 @@ nsHttpChannelAuthProvider::GenCredsAndSe
         0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CREDENTIALS);
     bool saveChallenge =
         0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CHALLENGE);
 
     bool saveIdentity =
         0 == (generateFlags & nsIHttpAuthenticator::USING_INTERNAL_IDENTITY);
 
     // this getter never fails
-    nsHttpAuthCache *authCache = gHttpHandler->AuthCache();
+    nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
 
     // create a cache entry.  we do this even though we don't yet know that
     // these credentials are valid b/c we need to avoid prompting the user
     // more than once in case the credentials are valid.
     //
     // if the credentials are not reusable, then we don't bother sticking
     // them in the auth cache.
     rv = authCache->SetAuthEntry(scheme, host, port, directory, realm,
@@ -626,17 +630,17 @@ nsHttpChannelAuthProvider::GetCredential
                                                       nsIHttpAuthenticator *auth,
                                                       nsAFlatCString     &creds)
 {
     LOG(("nsHttpChannelAuthProvider::GetCredentialsForChallenge "
          "[this=%p channel=%p proxyAuth=%d challenges=%s]\n",
         this, mAuthChannel, proxyAuth, challenge));
 
     // this getter never fails
-    nsHttpAuthCache *authCache = gHttpHandler->AuthCache();
+    nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
 
     uint32_t authFlags;
     nsresult rv = auth->GetAuthFlags(&authFlags);
     if (NS_FAILED(rv)) return rv;
 
     nsAutoCString realm;
     ParseRealm(challenge, realm);
 
@@ -1048,17 +1052,17 @@ NS_IMETHODIMP nsHttpChannelAuthProvider:
     rv = GetAuthorizationMembers(mProxyAuth, scheme, host, port,
                                  path, ident, continuationState);
     if (NS_FAILED(rv))
         OnAuthCancelled(aContext, false);
 
     nsAutoCString realm;
     ParseRealm(mCurrentChallenge.get(), realm);
 
-    nsHttpAuthCache *authCache = gHttpHandler->AuthCache();
+    nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
     nsHttpAuthEntry *entry = nullptr;
     authCache->GetAuthEntryForDomain(scheme.get(), host, port,
                                      realm.get(), &entry);
 
     nsCOMPtr<nsISupports> sessionStateGrip;
     if (entry)
         sessionStateGrip = entry->mMetaData;
 
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.h
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.h
@@ -117,16 +117,17 @@ private:
     nsIHttpAuthenticableChannel      *mAuthChannel;  // weak ref
 
     nsCOMPtr<nsIURI>                  mURI;
     nsCOMPtr<nsProxyInfo>             mProxyInfo;
     nsCString                         mHost;
     nsCString                         mCanonicalizedHost;
     int32_t                           mPort;
     bool                              mUsingSSL;
+    bool                              mIsPrivate;
 
     nsISupports                      *mProxyAuthContinuationState;
     nsCString                         mProxyAuthType;
     nsISupports                      *mAuthContinuationState;
     nsCString                         mAuthType;
     nsHttpAuthIdentity                mIdent;
     nsHttpAuthIdentity                mProxyIdent;
 
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -268,16 +268,19 @@ nsHttpHandler::Init()
     }
 
     mSessionStartTime = NowInSeconds();
     mHandlerActive = true;
 
     rv = mAuthCache.Init();
     if (NS_FAILED(rv)) return rv;
 
+    rv = mPrivateAuthCache.Init();
+    if (NS_FAILED(rv)) return rv;
+
     rv = InitConnectionMgr();
     if (NS_FAILED(rv)) return rv;
 
     mProductSub.AssignLiteral(MOZILLA_UAVERSION);
 
 #if DEBUG
     // dump user agent prefs
     LOG(("> legacy-app-name = %s\n", mLegacyAppName.get()));
@@ -302,16 +305,17 @@ nsHttpHandler::Init()
     mObserverService = services::GetObserverService();
     if (mObserverService) {
         mObserverService->AddObserver(this, "profile-change-net-teardown", true);
         mObserverService->AddObserver(this, "profile-change-net-restore", true);
         mObserverService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
         mObserverService->AddObserver(this, "net:clear-active-logins", true);
         mObserverService->AddObserver(this, "net:prune-dead-connections", true);
         mObserverService->AddObserver(this, "net:failed-to-process-uri-content", true);
+        mObserverService->AddObserver(this, "last-pb-context-exited", true);
     }
 
     return NS_OK;
 }
 
 nsresult
 nsHttpHandler::InitConnectionMgr()
 {
@@ -1561,42 +1565,47 @@ nsHttpHandler::Observe(nsISupports *subj
     }
     else if (strcmp(topic, "profile-change-net-teardown")    == 0 ||
              strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)    == 0) {
 
         mHandlerActive = false;
 
         // clear cache of all authentication credentials.
         mAuthCache.ClearAll();
+        mPrivateAuthCache.ClearAll();
 
         // ensure connection manager is shutdown
         if (mConnMgr)
             mConnMgr->Shutdown();
 
         // need to reset the session start time since cache validation may
         // depend on this value.
         mSessionStartTime = NowInSeconds();
     }
     else if (strcmp(topic, "profile-change-net-restore") == 0) {
         // initialize connection manager
         InitConnectionMgr();
     }
     else if (strcmp(topic, "net:clear-active-logins") == 0) {
         mAuthCache.ClearAll();
+        mPrivateAuthCache.ClearAll();
     }
     else if (strcmp(topic, "net:prune-dead-connections") == 0) {
         if (mConnMgr) {
             mConnMgr->PruneDeadConnections();
         }
     }
     else if (strcmp(topic, "net:failed-to-process-uri-content") == 0) {
         nsCOMPtr<nsIURI> uri = do_QueryInterface(subject);
         if (uri && mConnMgr)
             mConnMgr->ReportFailedToProcess(uri);
     }
+    else if (strcmp(topic, "last-pb-context-exited") == 0) {
+        mPrivateAuthCache.ClearAll();
+    }
 
     return NS_OK;
 }
 
 // nsISpeculativeConnect
 
 NS_IMETHODIMP
 nsHttpHandler::SpeculativeConnect(nsIURI *aURI,
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -95,17 +95,19 @@ public:
     uint32_t       SpdySendingChunkSize() { return mSpdySendingChunkSize; }
     uint32_t       SpdySendBufferSize()      { return mSpdySendBufferSize; }
     PRIntervalTime SpdyPingThreshold() { return mSpdyPingThreshold; }
     PRIntervalTime SpdyPingTimeout() { return mSpdyPingTimeout; }
     uint32_t       ConnectTimeout()  { return mConnectTimeout; }
 
     bool           PromptTempRedirect()      { return mPromptTempRedirect; }
 
-    nsHttpAuthCache     *AuthCache() { return &mAuthCache; }
+    nsHttpAuthCache     *AuthCache(bool aPrivate) {
+        return aPrivate ? &mPrivateAuthCache : &mAuthCache;
+    }
     nsHttpConnectionMgr *ConnMgr()   { return mConnMgr; }
 
     // cache support
     bool UseCache() const { return mUseCache; }
     uint32_t GenerateUniqueID() { return ++mLastUniqueID; }
     uint32_t SessionStartTime() { return mSessionStartTime; }
 
     //
@@ -264,16 +266,17 @@ private:
     nsCOMPtr<nsIStreamConverterService> mStreamConvSvc;
     nsCOMPtr<nsIObserverService>        mObserverService;
     nsCOMPtr<nsICookieService>          mCookieService;
     nsCOMPtr<nsIIDNService>             mIDNConverter;
     nsCOMPtr<nsIStrictTransportSecurityService> mSTSService;
 
     // the authentication credentials cache
     nsHttpAuthCache mAuthCache;
+    nsHttpAuthCache mPrivateAuthCache;
 
     // the connection manager
     nsHttpConnectionMgr *mConnMgr;
 
     //
     // prefs
     //
 
--- a/netwerk/protocol/http/nsIHttpAuthManager.idl
+++ b/netwerk/protocol/http/nsIHttpAuthManager.idl
@@ -14,17 +14,17 @@
  *
  * This interface exists to provide other HTTP stacks with the
  * ability to share HTTP authentication credentials with Necko.
  * This is currently used by the Java plugin (version 1.5 and
  * higher) to avoid duplicate authentication prompts when the
  * Java client fetches content from a HTTP site that the user
  * has already logged into.
  */
-[scriptable, uuid(7ce8e9d1-8b4b-4883-a307-66fe12a50153)]
+[scriptable, uuid(1301b517-ac72-48f6-a781-70c196eaaf3d)]
 interface nsIHttpAuthManager : nsISupports
 {
     /**
      * Lookup auth identity.
      *
      * @param aScheme
      *        the URL scheme (e.g., "http").  NOTE: for proxy authentication,
      *        this should be "http" (this includes authentication for CONNECT
@@ -49,17 +49,18 @@ interface nsIHttpAuthManager : nsISuppor
     void getAuthIdentity(in ACString aScheme,
                          in ACString aHost,
                          in int32_t  aPort,
                          in ACString aAuthType,
                          in ACString aRealm,
                          in ACString aPath,
                          out AString aUserDomain,
                          out AString aUserName,
-                         out AString aUserPassword);
+                         out AString aUserPassword,
+                         [optional] in bool aIsPrivate);
 
     /**
      * Store auth identity.
      *
      * @param aScheme
      *        the URL scheme (e.g., "http").  NOTE: for proxy authentication,
      *        this should be "http" (this includes authentication for CONNECT
      *        tunneling).
@@ -83,15 +84,16 @@ interface nsIHttpAuthManager : nsISuppor
     void setAuthIdentity(in ACString aScheme,
                          in ACString aHost,
                          in int32_t  aPort,
                          in ACString aAuthType,
                          in ACString aRealm,
                          in ACString aPath,
                          in AString  aUserDomain,
                          in AString  aUserName,
-                         in AString  aUserPassword);
+                         in AString  aUserPassword,
+                         [optional] in boolean aIsPrivate);
 
     /**
      * Clear all auth cache.
      */
     void clearAll();
 };