Skip to content

Commit

Permalink
UTF-8 encode basic auth username and password
Browse files Browse the repository at this point in the history
Also accept bytes to allow user control over encoding.

Closes twisted#268.
  • Loading branch information
twm committed Dec 26, 2020
1 parent 1fba8e0 commit db208fb
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 6 deletions.
1 change: 1 addition & 0 deletions changelog.d/268.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The *auth* parameter now accepts arbitrary text and `bytes` for usernames and passwords. Text is encoded as UTF-8, per :rfc:`7617`. Previously only ASCII was allowed.
19 changes: 13 additions & 6 deletions src/treq/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
# See LICENSE for details.
from __future__ import absolute_import, division, print_function

import base64
import binascii
from typing import Union

from twisted.web.http_headers import Headers
from twisted.web.iweb import IAgent
Expand Down Expand Up @@ -46,6 +47,7 @@ def request(self, method, uri, headers=None, bodyProducer=None):


def add_basic_auth(agent, username, password):
# type: (IAgent, Union[str, bytes], Union[str, bytes]) -> IAgent
"""
Wrap an agent to add HTTP basic authentication
Expand All @@ -59,16 +61,21 @@ def add_basic_auth(agent, username, password):
of the *Authorization* header is server-defined.
:param agent: Agent to wrap.
:param username: Username as an ASCII string.
:param password: Password as an ASCII string.
:param username: The username.
:param password: The password.
:returns: :class:`~twisted.web.iweb.IAgent`
"""
creds = base64.b64encode(
'{0}:{1}'.format(username, password).encode('ascii'))
if not isinstance(username, bytes):
username = username.encode('utf-8')
if not isinstance(password, bytes):
password = password.encode('utf-8')

creds = binascii.b2a_base64(b'%s:%s' % (username, password)).rstrip(b'\n')
return _RequestHeaderSettingAgent(
agent,
Headers({b'Authorization': [b'Basic ' + creds]}))
Headers({b'Authorization': [b'Basic ' + creds]}),
)


def add_auth(agent, auth_config):
Expand Down
33 changes: 33 additions & 0 deletions src/treq/test/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,39 @@ def test_add_basic_auth_huge(self):
Headers({b'authorization': [auth]}),
)

def test_add_basic_auth_utf8(self):
"""
Basic auth username and passwords given as `str` are encoded as UTF-8.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#Character_encoding_of_HTTP_authentication
"""
agent, requests = recorder()
auth = (u'\u16d7', u'\u16b9')
authAgent = add_auth(agent, auth)

authAgent.request(b'method', b'uri')

self.assertEqual(
requests[0].headers,
Headers({b'Authorization': [b'Basic 4ZuXOuGauQ==']}),
)

def test_add_basic_auth_bytes(self):
"""
Basic auth can be passed as `bytes`, allowing the user full control
over the encoding.
"""
agent, requests = recorder()
auth = (b'\x01\x0f\xff', b'\xff\xf0\x01')
authAgent = add_auth(agent, auth)

authAgent.request(b'method', b'uri')

self.assertEqual(
requests[0].headers,
Headers({b'Authorization': [b'Basic AQ//Ov/wAQ==']}),
)

def test_add_unknown_auth(self):
"""
add_auth() raises UnknownAuthConfig when given anything other than
Expand Down

0 comments on commit db208fb

Please sign in to comment.