We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
API v2 - Authenticate Fail
I'm trying to use the APIv2 but the authorization fails.
What is wrong with my resquests?

#############Request#############
POST /api/v2/authenticate HTTP/1.1
Host: 192.168.17.204:8081
User-Agent: FlexGet/3.1.15 (www.flexget.com)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 46
Content-Type: application/json

{"username": "medusa", "password": "password"}

#############Response#############
HTTP/1.1 200 OK
Server: TornadoServer/5.1.1
Content-Type: application/json; charset=UTF-8
Date: Thu, 06 Feb 2020 15:58:10 GMT
X-Medusa-Server: 0.3.11
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, Accept, Authorization, Content-Type, X-Requested-With, X-CSRF-Token, X-Api-Key, X-Medusa-Server
Access-Control-Allow-Methods: OPTIONS, POST
Content-Length: 297
Vary: Accept-Encoding

{"token": "b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJNZWR1c2EgMC4zLjExIiwiaWF0IjoxNTgxMDA0NjkwLCJqdGkiOiJwZ25rdXI2WDZrOEdRZjBleGc1OCIsImV4cCI6MTU4MTA5MTA5MCwidXNlcm5hbWUiOiJtZWR1c2EiLCJhcGlLZXkiOiI3NWVhYWM4ZTY3YzRhNWIyODQ5MmZmZjk3ODRjNDZhMCJ9.LU7fdfIU9wFVvg_nsJpPzUgOUQ8juPR0t6_uACfr3Zc'"}




#############Request#############
GET /api/v2/series?limit=1000 HTTP/1.1
Host: 192.168.17.204:8081
User-Agent: FlexGet/3.1.15 (www.flexget.com)
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
authorization: Bearer b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJNZWR1c2EgMC4zLjExIiwiaWF0IjoxNTgxMDA0NjkwLCJqdGkiOiJwZ25rdXI2WDZrOEdRZjBleGc1OCIsImV4cCI6MTU4MTA5MTA5MCwidXNlcm5hbWUiOiJtZWR1c2EiLCJhcGlLZXkiOiI3NWVhYWM4ZTY3YzRhNWIyODQ5MmZmZjk3ODRjNDZhMCJ9.LU7fdfIU9wFVvg_nsJpPzUgOUQ8juPR0t6_uACfr3Zc'

#############Response#############
HTTP/1.1 401 Unauthorized
Server: TornadoServer/5.1.1
Content-Type: application/json; charset=UTF-8
Date: Thu, 06 Feb 2020 15:58:10 GMT
X-Medusa-Server: 0.3.11
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, Accept, Authorization, Content-Type, X-Requested-With, X-CSRF-Token, X-Api-Key, X-Medusa-Server
Access-Control-Allow-Methods: OPTIONS, GET, POST, PATCH, DELETE
Content-Length: 27
Vary: Accept-Encoding

{"error": "Invalid token."}

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `medusa/server/api/v2/auth.py`
Content:
```
1 # coding=utf-8
2 """Request handler for authentication."""
3 from __future__ import unicode_literals
4 
5 import logging
6 import random
7 import string
8 import time
9 from builtins import range
10 
11 import jwt
12 
13 from medusa import app, helpers, notifiers
14 from medusa.logger.adapters.style import BraceAdapter
15 from medusa.server.api.v2.base import BaseRequestHandler
16 
17 from six import text_type
18 
19 from tornado.escape import json_decode
20 
21 log = BraceAdapter(logging.getLogger(__name__))
22 log.logger.addHandler(logging.NullHandler())
23 
24 
25 class AuthHandler(BaseRequestHandler):
26     """Auth request handler."""
27 
28     #: resource name
29     name = 'authenticate'
30     #: allowed HTTP methods
31     allowed_methods = ('POST', )
32 
33     def _check_authentication(self):
34         """Override authentication check for the authentication endpoint."""
35         return None
36 
37     def post(self, *args, **kwargs):
38         """Request JWT."""
39         username = app.WEB_USERNAME
40         password = app.WEB_PASSWORD
41 
42         # If the user hasn't set a username and/or password just let them login
43         if not username.strip() or not password.strip():
44             return self._login()
45 
46         if not self.request.body:
47             return self._failed_login(error='No Credentials Provided')
48 
49         if self.request.headers['content-type'] != 'application/json':
50             return self._failed_login(error='Incorrect content-type')
51 
52         request_body = json_decode(self.request.body)
53         submitted_username = request_body.get('username')
54         submitted_password = request_body.get('password')
55         submitted_exp = request_body.get('exp', 86400)
56         if username != submitted_username or password != submitted_password:
57             return self._failed_login(error='Invalid credentials')
58 
59         return self._login(submitted_exp)
60 
61     def _login(self, exp=86400):
62         self.set_header('Content-Type', 'application/json')
63         if app.NOTIFY_ON_LOGIN and not helpers.is_ip_private(self.request.remote_ip):
64             notifiers.notify_login(self.request.remote_ip)
65 
66         log.info('{user} logged into the API v2', {'user': app.WEB_USERNAME})
67         time_now = int(time.time())
68         return self._ok(data={
69             'token': jwt.encode({
70                 'iss': 'Medusa ' + text_type(app.APP_VERSION),
71                 'iat': time_now,
72                 # @TODO: The jti should be saved so we can revoke tokens
73                 'jti': ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(20)),
74                 'exp': time_now + int(exp),
75                 'username': app.WEB_USERNAME,
76                 'apiKey': app.API_KEY
77             }, app.ENCRYPTION_SECRET, algorithm='HS256')
78         })
79 
80     def _failed_login(self, error=None):
81         log.warning('{user} attempted a failed login to the API v2 from IP: {ip}', {
82             'user': app.WEB_USERNAME,
83             'ip': self.request.remote_ip
84         })
85         return self._unauthorized(error=error)
86
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/medusa/server/api/v2/auth.py b/medusa/server/api/v2/auth.py
--- a/medusa/server/api/v2/auth.py
+++ b/medusa/server/api/v2/auth.py
@@ -74,7 +74,7 @@
                 'exp': time_now + int(exp),
                 'username': app.WEB_USERNAME,
                 'apiKey': app.API_KEY
-            }, app.ENCRYPTION_SECRET, algorithm='HS256')
+            }, app.ENCRYPTION_SECRET, algorithm='HS256').decode('utf-8')
         })
 
     def _failed_login(self, error=None):

{"golden_diff": "diff --git a/medusa/server/api/v2/auth.py b/medusa/server/api/v2/auth.py\n--- a/medusa/server/api/v2/auth.py\n+++ b/medusa/server/api/v2/auth.py\n@@ -74,7 +74,7 @@\n                 'exp': time_now + int(exp),\n                 'username': app.WEB_USERNAME,\n                 'apiKey': app.API_KEY\n-            }, app.ENCRYPTION_SECRET, algorithm='HS256')\n+            }, app.ENCRYPTION_SECRET, algorithm='HS256').decode('utf-8')\n         })\n \n     def _failed_login(self, error=None):\n", "issue": "API v2 - Authenticate Fail\nI'm trying to use the APIv2 but the authorization fails.\r\nWhat is wrong with my resquests?\r\n\r\n#############Request#############\r\nPOST /api/v2/authenticate HTTP/1.1\r\nHost: 192.168.17.204:8081\r\nUser-Agent: FlexGet/3.1.15 (www.flexget.com)\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nContent-Length: 46\r\nContent-Type: application/json\r\n\r\n{\"username\": \"medusa\", \"password\": \"password\"}\r\n\r\n#############Response#############\r\nHTTP/1.1 200 OK\r\nServer: TornadoServer/5.1.1\r\nContent-Type: application/json; charset=UTF-8\r\nDate: Thu, 06 Feb 2020 15:58:10 GMT\r\nX-Medusa-Server: 0.3.11\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: Origin, Accept, Authorization, Content-Type, X-Requested-With, X-CSRF-Token, X-Api-Key, X-Medusa-Server\r\nAccess-Control-Allow-Methods: OPTIONS, POST\r\nContent-Length: 297\r\nVary: Accept-Encoding\r\n\r\n{\"token\": \"b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJNZWR1c2EgMC4zLjExIiwiaWF0IjoxNTgxMDA0NjkwLCJqdGkiOiJwZ25rdXI2WDZrOEdRZjBleGc1OCIsImV4cCI6MTU4MTA5MTA5MCwidXNlcm5hbWUiOiJtZWR1c2EiLCJhcGlLZXkiOiI3NWVhYWM4ZTY3YzRhNWIyODQ5MmZmZjk3ODRjNDZhMCJ9.LU7fdfIU9wFVvg_nsJpPzUgOUQ8juPR0t6_uACfr3Zc'\"}\r\n\r\n\r\n\r\n\r\n#############Request#############\r\nGET /api/v2/series?limit=1000 HTTP/1.1\r\nHost: 192.168.17.204:8081\r\nUser-Agent: FlexGet/3.1.15 (www.flexget.com)\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nauthorization: Bearer b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJNZWR1c2EgMC4zLjExIiwiaWF0IjoxNTgxMDA0NjkwLCJqdGkiOiJwZ25rdXI2WDZrOEdRZjBleGc1OCIsImV4cCI6MTU4MTA5MTA5MCwidXNlcm5hbWUiOiJtZWR1c2EiLCJhcGlLZXkiOiI3NWVhYWM4ZTY3YzRhNWIyODQ5MmZmZjk3ODRjNDZhMCJ9.LU7fdfIU9wFVvg_nsJpPzUgOUQ8juPR0t6_uACfr3Zc'\r\n\r\n#############Response#############\r\nHTTP/1.1 401 Unauthorized\r\nServer: TornadoServer/5.1.1\r\nContent-Type: application/json; charset=UTF-8\r\nDate: Thu, 06 Feb 2020 15:58:10 GMT\r\nX-Medusa-Server: 0.3.11\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: Origin, Accept, Authorization, Content-Type, X-Requested-With, X-CSRF-Token, X-Api-Key, X-Medusa-Server\r\nAccess-Control-Allow-Methods: OPTIONS, GET, POST, PATCH, DELETE\r\nContent-Length: 27\r\nVary: Accept-Encoding\r\n\r\n{\"error\": \"Invalid token.\"}\n", "before_files": [{"content": "# coding=utf-8\n\"\"\"Request handler for authentication.\"\"\"\nfrom __future__ import unicode_literals\n\nimport logging\nimport random\nimport string\nimport time\nfrom builtins import range\n\nimport jwt\n\nfrom medusa import app, helpers, notifiers\nfrom medusa.logger.adapters.style import BraceAdapter\nfrom medusa.server.api.v2.base import BaseRequestHandler\n\nfrom six import text_type\n\nfrom tornado.escape import json_decode\n\nlog = BraceAdapter(logging.getLogger(__name__))\nlog.logger.addHandler(logging.NullHandler())\n\n\nclass AuthHandler(BaseRequestHandler):\n    \"\"\"Auth request handler.\"\"\"\n\n    #: resource name\n    name = 'authenticate'\n    #: allowed HTTP methods\n    allowed_methods = ('POST', )\n\n    def _check_authentication(self):\n        \"\"\"Override authentication check for the authentication endpoint.\"\"\"\n        return None\n\n    def post(self, *args, **kwargs):\n        \"\"\"Request JWT.\"\"\"\n        username = app.WEB_USERNAME\n        password = app.WEB_PASSWORD\n\n        # If the user hasn't set a username and/or password just let them login\n        if not username.strip() or not password.strip():\n            return self._login()\n\n        if not self.request.body:\n            return self._failed_login(error='No Credentials Provided')\n\n        if self.request.headers['content-type'] != 'application/json':\n            return self._failed_login(error='Incorrect content-type')\n\n        request_body = json_decode(self.request.body)\n        submitted_username = request_body.get('username')\n        submitted_password = request_body.get('password')\n        submitted_exp = request_body.get('exp', 86400)\n        if username != submitted_username or password != submitted_password:\n            return self._failed_login(error='Invalid credentials')\n\n        return self._login(submitted_exp)\n\n    def _login(self, exp=86400):\n        self.set_header('Content-Type', 'application/json')\n        if app.NOTIFY_ON_LOGIN and not helpers.is_ip_private(self.request.remote_ip):\n            notifiers.notify_login(self.request.remote_ip)\n\n        log.info('{user} logged into the API v2', {'user': app.WEB_USERNAME})\n        time_now = int(time.time())\n        return self._ok(data={\n            'token': jwt.encode({\n                'iss': 'Medusa ' + text_type(app.APP_VERSION),\n                'iat': time_now,\n                # @TODO: The jti should be saved so we can revoke tokens\n                'jti': ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(20)),\n                'exp': time_now + int(exp),\n                'username': app.WEB_USERNAME,\n                'apiKey': app.API_KEY\n            }, app.ENCRYPTION_SECRET, algorithm='HS256')\n        })\n\n    def _failed_login(self, error=None):\n        log.warning('{user} attempted a failed login to the API v2 from IP: {ip}', {\n            'user': app.WEB_USERNAME,\n            'ip': self.request.remote_ip\n        })\n        return self._unauthorized(error=error)\n", "path": "medusa/server/api/v2/auth.py"}], "after_files": [{"content": "# coding=utf-8\n\"\"\"Request handler for authentication.\"\"\"\nfrom __future__ import unicode_literals\n\nimport logging\nimport random\nimport string\nimport time\nfrom builtins import range\n\nimport jwt\n\nfrom medusa import app, helpers, notifiers\nfrom medusa.logger.adapters.style import BraceAdapter\nfrom medusa.server.api.v2.base import BaseRequestHandler\n\nfrom six import text_type\n\nfrom tornado.escape import json_decode\n\nlog = BraceAdapter(logging.getLogger(__name__))\nlog.logger.addHandler(logging.NullHandler())\n\n\nclass AuthHandler(BaseRequestHandler):\n    \"\"\"Auth request handler.\"\"\"\n\n    #: resource name\n    name = 'authenticate'\n    #: allowed HTTP methods\n    allowed_methods = ('POST', )\n\n    def _check_authentication(self):\n        \"\"\"Override authentication check for the authentication endpoint.\"\"\"\n        return None\n\n    def post(self, *args, **kwargs):\n        \"\"\"Request JWT.\"\"\"\n        username = app.WEB_USERNAME\n        password = app.WEB_PASSWORD\n\n        # If the user hasn't set a username and/or password just let them login\n        if not username.strip() or not password.strip():\n            return self._login()\n\n        if not self.request.body:\n            return self._failed_login(error='No Credentials Provided')\n\n        if self.request.headers['content-type'] != 'application/json':\n            return self._failed_login(error='Incorrect content-type')\n\n        request_body = json_decode(self.request.body)\n        submitted_username = request_body.get('username')\n        submitted_password = request_body.get('password')\n        submitted_exp = request_body.get('exp', 86400)\n        if username != submitted_username or password != submitted_password:\n            return self._failed_login(error='Invalid credentials')\n\n        return self._login(submitted_exp)\n\n    def _login(self, exp=86400):\n        self.set_header('Content-Type', 'application/json')\n        if app.NOTIFY_ON_LOGIN and not helpers.is_ip_private(self.request.remote_ip):\n            notifiers.notify_login(self.request.remote_ip)\n\n        log.info('{user} logged into the API v2', {'user': app.WEB_USERNAME})\n        time_now = int(time.time())\n        return self._ok(data={\n            'token': jwt.encode({\n                'iss': 'Medusa ' + text_type(app.APP_VERSION),\n                'iat': time_now,\n                # @TODO: The jti should be saved so we can revoke tokens\n                'jti': ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(20)),\n                'exp': time_now + int(exp),\n                'username': app.WEB_USERNAME,\n                'apiKey': app.API_KEY\n            }, app.ENCRYPTION_SECRET, algorithm='HS256').decode('utf-8')\n        })\n\n    def _failed_login(self, error=None):\n        log.warning('{user} attempted a failed login to the API v2 from IP: {ip}', {\n            'user': app.WEB_USERNAME,\n            'ip': self.request.remote_ip\n        })\n        return self._unauthorized(error=error)\n", "path": "medusa/server/api/v2/auth.py"}]}