# Buggy code
def _check_authentication(self):
    """Check if JWT or API key is provided and valid."""
    if self.request.method == "OPTIONS":
        return

    api_key = self.get_argument("api_key", default=None) or self.request.headers.get(
        "X-Api-Key"
    )
    if api_key and api_key == app.API_KEY:
        return

    authorization = self.request.headers.get("Authorization")
    if not authorization:
        return self._unauthorized("No authorization token.")

    if authorization.startswith("Bearer"):
        try:
            token = authorization.replace("Bearer ", "")
            jwt.decode(token, app.ENCRYPTION_SECRET, algorithms=["HS256"])
        except jwt.ExpiredSignatureError:
            return self._unauthorized("Token has expired.")
        except jwt.DecodeError:
            return self._unauthorized("Invalid token.")
    elif authorization.startswith("Basic"):
        auth_decoded = base64.decodestring(authorization[6:])
        username, password = auth_decoded.split(":", 2)
        if username != app.WEB_USERNAME or password != app.WEB_PASSWORD:
            return self._unauthorized("Invalid user/pass.")
    else:
        return self._unauthorized("Invalid token.")


# Traceback (TypeError)
2020-09-17 08:34:09 ERROR    TORNADO :: [b352bb6] Uncaught exception in APIv2: TypeError('expected bytes-like object, not str')
Request: GET /**********/medusa/api/v2/series?limit=1000&page=1 (127.0.0.1)
Traceback (most recent call last):
  File "/usr/lib/python3.8/base64.py", line 510, in _input_type_check
    m = memoryview(s)
TypeError: memoryview: a bytes-like object is required, not 'str'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "/home/**********/.medusa/ext/tornado/web.py", line 1592, in _execute
    result = yield result
  File "/home/**********/.medusa/ext/tornado/gen.py", line 1133, in run
    value = future.result()
  File "/home/**********/.medusa/ext/tornado/gen.py", line 326, in wrapper
    yielded = next(result)
  File "/home/**********/.medusa/medusa/server/api/v2/base.py", line 57, in async_call
    content = self._check_authentication()
  File "/home/**********/.medusa/medusa/server/api/v2/base.py", line 130, in _check_authentication
    auth_decoded = base64.decodestring(authorization[6:])
  File "/usr/lib/python3.8/base64.py", line 554, in decodestring
    return decodebytes(s)
  File "/usr/lib/python3.8/base64.py", line 545, in decodebytes
    _input_type_check(s)
  File "/usr/lib/python3.8/base64.py", line 513, in _input_type_check
    raise TypeError(msg) from err
TypeError: expected bytes-like object, not str
2020-09-17 08:34:10 WARNING  TORNADO :: [b352bb6] User attempted a failed login to the Medusa web interface from IP: 127.0.0.1
2020-09-17 08:34:11 ERROR    TORNADO :: [b352bb6] Uncaught exception in APIv2: TypeError('expected bytes-like object, not str')
Request: GET /**********/medusa/api/v2/series?limit=1000&page=1 (127.0.0.1)
Traceback (most recent call last):
  File "/usr/lib/python3.8/base64.py", line 510, in _input_type_check
    m = memoryview(s)
TypeError: memoryview: a bytes-like object is required, not 'str'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "/home/**********/.medusa/ext/tornado/web.py", line 1592, in _execute
    result = yield result
  File "/home/**********/.medusa/ext/tornado/gen.py", line 1133, in run
    value = future.result()
  File "/home/**********/.medusa/ext/tornado/gen.py", line 326, in wrapper
    yielded = next(result)
  File "/home/**********/.medusa/medusa/server/api/v2/base.py", line 57, in async_call
    content = self._check_authentication()
  File "/home/**********/.medusa/medusa/server/api/v2/base.py", line 130, in _check_authentication
    auth_decoded = base64.decodestring(authorization[6:])
  File "/usr/lib/python3.8/base64.py", line 554, in decodestring
    return decodebytes(s)
  File "/usr/lib/python3.8/base64.py", line 545, in decodebytes
    _input_type_check(s)
  File "/usr/lib/python3.8/base64.py", line 513, in _input_type_check
    raise TypeError(msg) from err
TypeError: expected bytes-like object, not str
2020-09-17 08:34:14 WARNING  TORNADO :: [b352bb6] User attempted a failed login to the Medusa web interface from IP: 127.0.0.1
2020-09-17 08:34:15 ERROR    TORNADO :: [b352bb6] Uncaught exception in APIv2: TypeError('expected bytes-like object, not str')
Request: GET /**********/medusa/api/v2/series?limit=1000&page=1 (127.0.0.1)
Traceback (most recent call last):
  File "/usr/lib/python3.8/base64.py", line 510, in _input_type_check
    m = memoryview(s)
TypeError: memoryview: a bytes-like object is required, not 'str'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "/home/**********/.medusa/ext/tornado/web.py", line 1592, in _execute
    result = yield result
  File "/home/**********/.medusa/ext/tornado/gen.py", line 1133, in run
    value = future.result()
  File "/home/**********/.medusa/ext/tornado/gen.py", line 326, in wrapper
    yielded = next(result)
  File "/home/**********/.medusa/medusa/server/api/v2/base.py", line 57, in async_call
    content = self._check_authentication()
  File "/home/**********/.medusa/medusa/server/api/v2/base.py", line 130, in _check_authentication
    auth_decoded = base64.decodestring(authorization[6:])
  File "/usr/lib/python3.8/base64.py", line 554, in decodestring
    return decodebytes(s)
  File "/usr/lib/python3.8/base64.py", line 545, in decodebytes
    _input_type_check(s)
  File "/usr/lib/python3.8/base64.py", line 513, in _input_type_check
    raise TypeError(msg) from err
TypeError: expected bytes-like object, not str

# Fixed code
def _check_authentication(self):
    """Check if JWT or API key is provided and valid."""
    if self.request.method == "OPTIONS":
        return

    api_key = self.get_argument("api_key", default=None) or self.request.headers.get(
        "X-Api-Key"
    )
    if api_key and api_key == app.API_KEY:
        return

    authorization = self.request.headers.get("Authorization")
    if not authorization:
        return self._unauthorized("No authorization token.")

    if authorization.startswith("Bearer"):
        try:
            token = authorization.replace("Bearer ", "")
            jwt.decode(token, app.ENCRYPTION_SECRET, algorithms=["HS256"])
        except jwt.ExpiredSignatureError:
            return self._unauthorized("Token has expired.")
        except jwt.DecodeError:
            return self._unauthorized("Invalid token.")
    elif authorization.startswith("Basic"):
        auth_decoded = base64.b64decode(authorization[6:])
        username, password = auth_decoded.split(":", 2)
        if username != app.WEB_USERNAME or password != app.WEB_PASSWORD:
            return self._unauthorized("Invalid user/pass.")
    else:
        return self._unauthorized("Invalid token.")