<p dir="auto">I tried setting the <code class="notranslate">Authorization</code> header manually to make a request to the Github API, but authentication failed despite the same token working fine when making the request using cURL, <code class="notranslate">urllib.request.Request()</code>, and other methods.</p>
<h2 dir="auto">Expected Result</h2>
<p dir="auto">Successful auth and a <code class="notranslate">200 OK</code> response.</p>
<h2 dir="auto">Actual Result</h2>
<p dir="auto">Authentication fails with a <code class="notranslate">401 Bad Credentials</code> response.</p>
<p dir="auto">Upon inspecting the response's <code class="notranslate">request</code> attribute, I found that the <code class="notranslate">Authorization</code> header had been overwritten (looks like this happens <a href="https://github.com/psf/requests/blob/9cdf294/requests/auth.py#L95-L97">here</a>).</p>
<p dir="auto">I've since worked around this, but in my opinion, one should expect that if they are setting the <code class="notranslate">Authorization</code> header manually (rather than using the <code class="notranslate">auth</code> param), the header should not be overwritten.</p>
<p dir="auto">I can draft a PR to address this if the project maintainers agree to such a change.</p>
<h2 dir="auto">Reproduction Steps</h2>
<p dir="auto">Create an <a href="https://github.com/settings/tokens">access token</a> and then try to auth to the API like so:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="requests.get(
    'https://api.github.com/notifications',
    headers={'Authorization': 'token MY_TOKEN_HERE'}
)"><pre class="notranslate"><span class="pl-s1">requests</span>.<span class="pl-en">get</span>(
    <span class="pl-s">'https://api.github.com/notifications'</span>,
    <span class="pl-s1">headers</span><span class="pl-c1">=</span>{<span class="pl-s">'Authorization'</span>: <span class="pl-s">'token MY_TOKEN_HERE'</span>}
)</pre></div>
<h2 dir="auto">System Information</h2>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{
  &quot;chardet&quot;: {
    &quot;version&quot;: &quot;3.0.4&quot;
  },
  &quot;cryptography&quot;: {
    &quot;version&quot;: &quot;&quot;
  },
  &quot;idna&quot;: {
    &quot;version&quot;: &quot;2.9&quot;
  },
  &quot;implementation&quot;: {
    &quot;name&quot;: &quot;CPython&quot;,
    &quot;version&quot;: &quot;3.8.2&quot;
  },
  &quot;platform&quot;: {
    &quot;release&quot;: &quot;5.6.3-arch1-1&quot;,
    &quot;system&quot;: &quot;Linux&quot;
  },
  &quot;pyOpenSSL&quot;: {
    &quot;openssl_version&quot;: &quot;&quot;,
    &quot;version&quot;: null
  },
  &quot;requests&quot;: {
    &quot;version&quot;: &quot;2.23.0&quot;
  },
  &quot;system_ssl&quot;: {
    &quot;version&quot;: &quot;1010106f&quot;
  },
  &quot;urllib3&quot;: {
    &quot;version&quot;: &quot;1.25.8&quot;
  },
  &quot;using_pyopenssl&quot;: false
}"><pre class="notranslate">{
  <span class="pl-s">"chardet"</span>: {
    <span class="pl-s">"version"</span>: <span class="pl-s">"3.0.4"</span>
  },
  <span class="pl-s">"cryptography"</span>: {
    <span class="pl-s">"version"</span>: <span class="pl-s">""</span>
  },
  <span class="pl-s">"idna"</span>: {
    <span class="pl-s">"version"</span>: <span class="pl-s">"2.9"</span>
  },
  <span class="pl-s">"implementation"</span>: {
    <span class="pl-s">"name"</span>: <span class="pl-s">"CPython"</span>,
    <span class="pl-s">"version"</span>: <span class="pl-s">"3.8.2"</span>
  },
  <span class="pl-s">"platform"</span>: {
    <span class="pl-s">"release"</span>: <span class="pl-s">"5.6.3-arch1-1"</span>,
    <span class="pl-s">"system"</span>: <span class="pl-s">"Linux"</span>
  },
  <span class="pl-s">"pyOpenSSL"</span>: {
    <span class="pl-s">"openssl_version"</span>: <span class="pl-s">""</span>,
    <span class="pl-s">"version"</span>: <span class="pl-s1">null</span>
  },
  <span class="pl-s">"requests"</span>: {
    <span class="pl-s">"version"</span>: <span class="pl-s">"2.23.0"</span>
  },
  <span class="pl-s">"system_ssl"</span>: {
    <span class="pl-s">"version"</span>: <span class="pl-s">"1010106f"</span>
  },
  <span class="pl-s">"urllib3"</span>: {
    <span class="pl-s">"version"</span>: <span class="pl-s">"1.25.8"</span>
  },
  <span class="pl-s">"using_pyopenssl"</span>: <span class="pl-s1">false</span>
}</pre></div>
<h2 dir="auto">Summary</h2>
<p dir="auto">Explicitly calling out method-level auth parameter that matches associated session-level auth seems to yield functionally different behavior.</p>
<h2 dir="auto">Expected Results</h2>
<p dir="auto">I am parsing a CSV file containing references to local filenames that I am uploading via Requests to a RESTful API I'm helping test. The intended behavior is for the script to upload the files referenced in the CSV after establishing Digest authentication. The server should respond 401, then requests should respond accordingly, followed by a 200 response from the server.</p>
<h2 dir="auto">Actual Results</h2>
<p dir="auto">I recently encountered an error response from the server and found I got a different response simply by updating my python code to use Session() objects. Upon further testing, I have developed two code blocks that appear to be functionally equivalent but receive very different responses from the server. I am having a hard time determining why these two code blocks behave differently.</p>
<p dir="auto">The first code block shown below properly negotiates Digest authentication, receiving a 401 response from the server, sending the second part of the request, then receiving a 200 response.</p>
<p dir="auto">The second code block is as close to the first as I can make it while still reproducing the problem. Notice the code snippet contains a method-level auth argument which is an exact duplicate of the session-level authentication. However, for whatever reason, the API I'm testing responds 502 to this latter code block in response to the initial request, i.e. the point at which I would expect a 401 response. I will address the server-side error, but I'm confused regarding how the two blocks of code are functionally different.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="import sys, time, requests, csv
from requests.auth import HTTPDigestAuth, HTTPBasicAuth
custom_header = {'X-Requested-Auth': 'Digest'}
host = raw_input(&quot;Enter host address, including scheme: &quot;)
user = raw_input(&quot;Enter username: &quot;)
password = raw_input(&quot;Enter password: &quot;)
#v1 is a string representation of some xml
with open('myFiles.csv', 'rb') as csvfile:
    csvin = csv.DictReader(csvfile, delimiter=',', quotechar='&quot;')
    with requests.Session() as s:
        #Define session-level auth method and custom header
        s.auth = HTTPDigestAuth(user, password)
        s.headers.update(custom_header)
        for row in csvin:
                data = {'param1': v1, 'param2': 'some value'}
                with open(row['Presentation File'], 'rb') as f:
                    print &quot;Track found. Uploading...&quot;
                    r = s.post(host+'/api/uploadTrack', data=data, files={'file': (row['Presentation File'],f)}, verify=False, timeout=(6.05, 70))"><pre class="notranslate"><code class="notranslate">import sys, time, requests, csv
from requests.auth import HTTPDigestAuth, HTTPBasicAuth
custom_header = {'X-Requested-Auth': 'Digest'}
host = raw_input("Enter host address, including scheme: ")
user = raw_input("Enter username: ")
password = raw_input("Enter password: ")
#v1 is a string representation of some xml
with open('myFiles.csv', 'rb') as csvfile:
    csvin = csv.DictReader(csvfile, delimiter=',', quotechar='"')
    with requests.Session() as s:
        #Define session-level auth method and custom header
        s.auth = HTTPDigestAuth(user, password)
        s.headers.update(custom_header)
        for row in csvin:
                data = {'param1': v1, 'param2': 'some value'}
                with open(row['Presentation File'], 'rb') as f:
                    print "Track found. Uploading..."
                    r = s.post(host+'/api/uploadTrack', data=data, files={'file': (row['Presentation File'],f)}, verify=False, timeout=(6.05, 70))
</code></pre></div>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="import sys, time, requests, csv
from requests.auth import HTTPDigestAuth, HTTPBasicAuth
custom_header = {'X-Requested-Auth': 'Digest'}
host = raw_input(&quot;Enter host address, including scheme: &quot;)
user = raw_input(&quot;Enter username: &quot;)
password = raw_input(&quot;Enter password: &quot;)
#v1 is a string representation of some xml
with open('myFiles.csv', 'rb') as csvfile:
    csvin = csv.DictReader(csvfile, delimiter=',', quotechar='&quot;')
    with requests.Session() as s:
        #Define session-level auth method and custom header
        s.auth = HTTPDigestAuth(user, password)
        s.headers.update(custom_header)
        for row in csvin:
                data = {'param1': v1, 'param2': 'some value'}
                with open(row['Presentation File'], 'rb') as f:
                    print &quot;Track found. Uploading...&quot;
                    r = s.post(host+'/api/uploadTrack', auth=HTTPDigestAuth(user, password), data=data, files={'file': (row['Presentation File'],f)}, verify=False, timeout=(6.05, 70))"><pre class="notranslate"><code class="notranslate">import sys, time, requests, csv
from requests.auth import HTTPDigestAuth, HTTPBasicAuth
custom_header = {'X-Requested-Auth': 'Digest'}
host = raw_input("Enter host address, including scheme: ")
user = raw_input("Enter username: ")
password = raw_input("Enter password: ")
#v1 is a string representation of some xml
with open('myFiles.csv', 'rb') as csvfile:
    csvin = csv.DictReader(csvfile, delimiter=',', quotechar='"')
    with requests.Session() as s:
        #Define session-level auth method and custom header
        s.auth = HTTPDigestAuth(user, password)
        s.headers.update(custom_header)
        for row in csvin:
                data = {'param1': v1, 'param2': 'some value'}
                with open(row['Presentation File'], 'rb') as f:
                    print "Track found. Uploading..."
                    r = s.post(host+'/api/uploadTrack', auth=HTTPDigestAuth(user, password), data=data, files={'file': (row['Presentation File'],f)}, verify=False, timeout=(6.05, 70))
</code></pre></div>
<h2 dir="auto">System Information</h2>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="{
  &quot;chardet&quot;: {
    &quot;version&quot;: &quot;3.0.4&quot;
  },
  &quot;cryptography&quot;: {
    &quot;version&quot;: &quot;&quot;
  },
  &quot;idna&quot;: {
    &quot;version&quot;: &quot;2.6&quot;
  },
  &quot;implementation&quot;: {
    &quot;name&quot;: &quot;CPython&quot;,
    &quot;version&quot;: &quot;2.7.5&quot;
  },
  &quot;platform&quot;: {
    &quot;release&quot;: &quot;3.10.0-693.2.2.el7.x86_64&quot;,
    &quot;system&quot;: &quot;Linux&quot;
  },
  &quot;pyOpenSSL&quot;: {
    &quot;openssl_version&quot;: &quot;&quot;,
    &quot;version&quot;: null
  },
  &quot;requests&quot;: {
    &quot;version&quot;: &quot;2.18.4&quot;
  },
  &quot;system_ssl&quot;: {
    &quot;version&quot;: &quot;1000105f&quot;
  },
  &quot;urllib3&quot;: {
    &quot;version&quot;: &quot;1.22&quot;
  },
  &quot;using_pyopenssl&quot;: false
}"><pre class="notranslate"><code class="notranslate">{
  "chardet": {
    "version": "3.0.4"
  },
  "cryptography": {
    "version": ""
  },
  "idna": {
    "version": "2.6"
  },
  "implementation": {
    "name": "CPython",
    "version": "2.7.5"
  },
  "platform": {
    "release": "3.10.0-693.2.2.el7.x86_64",
    "system": "Linux"
  },
  "pyOpenSSL": {
    "openssl_version": "",
    "version": null
  },
  "requests": {
    "version": "2.18.4"
  },
  "system_ssl": {
    "version": "1000105f"
  },
  "urllib3": {
    "version": "1.22"
  },
  "using_pyopenssl": false
}
</code></pre></div>
<p dir="auto">Note that I can reproduce in a Windows environment using request 2.11.1, so this is not a recent regression.</p>