Multi-proxy with identical host:port overwrites credentials in `auth_store`

Author: lp123sunCreated Sep 13, 2026Updated Sep 13, 2026

Title

Multi-proxy with identical host:port overwrites credentials in auth_store

Description

Since v140.0.7339.123-2, NaiveProxy supports multiple proxies in one process: "listen": ["...", "..."], "proxy": ["...", "..."]

However, if multiple proxies share the same upstream host and port but use different Basic Auth credentials (e.g. multi-tenant setups, multiplexing different upstreams via reverse proxies like Caddy based on Proxy-Authorization), only the credentials of the last proxy in the list are retained.

Example Configuration:

jsonc
{
  "listen": [
    "socks://127.0.0.1:1080",
    "socks://127.0.0.1:1081"
  ],
  "proxy": [
    "https://userA:[email protected]",
    "https://userB:[email protected]"
  ]
}

What Happens:

Both port 1080 and port 1081 send Proxy-Authorization for userB:passB. Connections routed through port 1080 fail or are misrouted because its credentials (userA:passA) are overwritten by userB:passB.


Root Cause Analysis

  1. In src/net/tools/naive/naive_config.h:

    cpp
    std::map<url::SchemeHostPort, AuthCredentials> auth_store;
  2. In src/net/tools/naive/naive_config.cc:

    cpp
    auth_store[url::SchemeHostPort{GURL{proxy_url}}] = auth;

    url::SchemeHostPort only represents (scheme, host, port) without user credentials. When multiple proxy URIs point to the same host/port, subsequent entries overwrite previous ones in the global auth_store.

  3. In src/net/tools/naive/naive_proxy_bin.cc:

    cpp
    for (const auto& [k, v] : config.auth_store) {
      auto* session = context->http_transaction_factory()->GetSession();
      auto* auth_cache = session->http_auth_cache();
      auth_cache->Add(k, HttpAuth::AUTH_PROXY, ...);
    }

    All URLRequestContext instances end up populated with only the credentials of the last proxy in the list.


Proposed Solution

Since naive_proxy_bin.cc already creates an isolated URLRequestContext per proxy_chain_index (when config.proxy_chains.size() >= 2), we do not need to alter Chromium's url::SchemeHostPort.

Instead, credentials can be isolated per proxy chain by making auth_stores a std::vector:

  1. src/net/tools/naive/naive_config.h:

    diff
    - std::map<url::SchemeHostPort, AuthCredentials> auth_store;
    + std::vector<std::map<url::SchemeHostPort, AuthCredentials>> auth_stores;
  2. src/net/tools/naive/naive_config.cc: Parse and store chain_auth_store for each proxy chain in proxy_strs:

    diff
       for (const std::string& str : proxy_strs) {
    +    std::map<url::SchemeHostPort, AuthCredentials> chain_auth_store;
         base::StringTokenizer proxy_uri_list(str, ",");
         ...
    -    auth_store[url::SchemeHostPort{GURL{proxy_url}}] = auth;
    +    chain_auth_store[url::SchemeHostPort{GURL{proxy_url}}] = auth;
         ...
    +    auth_stores.push_back(std::move(chain_auth_store));
       }
  3. src/net/tools/naive/naive_proxy_bin.cc: Populate auth_cache from config.auth_stores[proxy_chain_index]:

    diff
    - for (const auto& [k, v] : config.auth_store) {
    -   auto* session = context->http_transaction_factory()->GetSession();
    -   auto* auth_cache = session->http_auth_cache();
    -   auth_cache->Add(k, HttpAuth::AUTH_PROXY, ...);
    - }
    + if (proxy_chain_index < static_cast<int>(config.auth_stores.size())) {
    +   for (const auto& [k, v] : config.auth_stores[proxy_chain_index]) {
    +     auto* session = context->http_transaction_factory()->GetSession();
    +     auto* auth_cache = session->http_auth_cache();
    +     auth_cache->Add(k, HttpAuth::AUTH_PROXY,
    +                     /*realm=*/{}, HttpAuth::AUTH_SCHEME_BASIC, {},
    +                     /*challenge=*/"Basic", v, /*path=*/"/");
    +   }
    + }

This cleanly decouples credentials per context and enables full multi-tenant/multi-upstream usage with identical hostnames.