1

I'm attempting to use the Backblaze B2 API to eventually iterate through some .ai files I have there, but the code that Backblaze provides in their documentation is giving me an error.

Here's the code

import base64
import json
import urllib3

id_and_key = 'applicationKeyId_value:applicationKey_value'
basic_auth_string = 'Basic ' + base64.b64encode(id_and_key)
headers = { 'Authorization': basic_auth_string }

request = urllib3.Request(
    'https://api.backblazeb2.com/b2api/v2/b2_authorize_account',
    headers = headers
    )
response = urllib3.urlopen(request)
response_data = json.loads(response.read())
response.close()

When I ran the code (replacing the id_and_key with my master key ID which looked something like '6b5*********') I got an error that looked like this

Traceback (most recent call last):
  File "/Users/jacobpatty/vscode_projects/badger_colors/backblaze_test.py", line 6, in <module>
    basic_auth_string = base64.b64encode(id_and_key)
  File "/opt/homebrew/Cellar/python@3.9/3.9.12/Frameworks/Python.framework/Versions/3.9/lib/python3.9/base64.py", line 58, in b64encode
    encoded = binascii.b2a_base64(s, newline=False)
TypeError: a bytes-like object is required, not 'str'

I looked at the base64 documentation, but I couldn't find any useful info there. Or is it possible it has something to do with urllib3? Anyone know why I'm getting this error, or how to fix it?

metadaddy
  • 4,234
  • 1
  • 22
  • 46

1 Answers1

1

Perhaps-confusingly, you need to .encode() the input to .b64encode()!

>>> import base64
>>> base64.b64encode("my input")
[clipped]
TypeError: a bytes-like object is required, not 'str'
>>> base64.b64encode("my input".encode())
b'bXkgaW5wdXQ='

Specifically for your auth header, you can do this all at once without bothering with the intermediate names

request = urllib3.Request(
    'https://api.backblazeb2.com/b2api/v2/b2_authorize_account',
    headers={
        "Authorization": f"Basic {base64.b64encode((f'{app_id}:{app_key}').encode()).decode()}"
    }
)
ti7
  • 16,375
  • 6
  • 40
  • 68
  • I added a little more, which I think is what you're after specific to your auth header as I ran into that too! Otherwise you just add `b` to `'Basic '` so it's a bytes-like too `b'Basic ' ...` – ti7 Apr 28 '22 at 17:40
  • 1
    Why is that confusing? Base64 is a 7-bit clean encoding of arbitrary binary data. A `str` is not binary data; it's a sequence of Unicode code points that first has to be encoded as a stream of bytes before you need to worry about whether that stream is 7-bit clean or not. – chepner Apr 28 '22 at 17:42
  • Indeed and it need not be! (though I suspect **@jowncluthber** may just not have been exposed to encodings until now) Practically, the broad issue is that strings _are_ bytes, but with an associated encoding .. they map nicely (`'A'` -> `b'A'`) when assuming between ~255 ASCII and UTF-8 chars (western symbols `ABCD...`, etc.), but characters outside of this range have different representations in different encodings, and so this is not guaranteed! .. base64 is simply representing the bytes in a way that intermediate network workings will nicely handle without it needing to know the encoding – ti7 Apr 28 '22 at 17:50
  • It's important to remember that `str` has no defined encoding. *If* you assume it's a sequence of UTF-8 sequences behind the scenes, then yes, the mapping from `'A'` to `b'A'` is trivial. But it could just as well be a sequence of UTF-16 sequences, in which case the mapping is less trivial. (And for all you know, the internal representation of a `str` could be something completely different altogether.) The whole point of Python 3's `str`-vs-`bytes` distinction is to *eliminate* any assumptions about what the `str` is, beyond an *abstraction* that represents Unicode code points. – chepner Apr 28 '22 at 18:07
  • If you want to operate on bytes that represent a `str`, you have to provide the bytes yourself; Python won't let you pretend that the bytes are already there. – chepner Apr 28 '22 at 18:09