prompt: 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()
  
  ```
  
golden_diff: 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):
  
verification_info: {"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"}]}