1

I have a question about flask python. I tried learning how to build a web using flask, and there is some error. In this case I am using mongoengine as database and JWT(Json Web Token) and the alert error is like this: "TypeError: Expecting a string- or bytes-formatted key"

192.168.100.26 - - [22/Nov/2016 22:50:08] "POST /auth HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/def/.local/lib/python2.7/site-packages/flask/app.py", line 2000, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/def/.local/lib/python2.7/site-packages/flask/app.py", line 1991, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/home/def/.local/lib/python2.7/site-packages/flask/app.py", line 1567, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/def/.local/lib/python2.7/site-packages/flask/app.py", line 1988, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/def/.local/lib/python2.7/site-packages/flask/app.py", line 1641, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/def/.local/lib/python2.7/site-packages/flask/app.py", line 1544, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/def/.local/lib/python2.7/site-packages/flask/app.py", line 1639, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/def/.local/lib/python2.7/site-packages/flask/app.py", line 1625, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/def/pr/flask/flask_deeper/test/routes/auth.py", line 26, in auth
    access_token = _jwt.jwt_encode_callback(identity)
  File "/usr/local/lib/python2.7/dist-packages/flask_jwt/__init__.py", line 70, in _default_jwt_encode_handler
    return jwt.encode(payload, secret, algorithm=algorithm, headers=headers)
  File "/usr/local/lib/python2.7/dist-packages/jwt/api_jwt.py", line 56, in encode
    json_payload, key, algorithm, headers, json_encoder
  File "/usr/local/lib/python2.7/dist-packages/jwt/api_jws.py", line 98, in encode
    key = alg_obj.prepare_key(key)
  File "/usr/local/lib/python2.7/dist-packages/jwt/algorithms.py", line 116, in prepare_key
    raise TypeError('Expecting a string- or bytes-formatted key.')
TypeError: Expecting a string- or bytes-formatted key.

I thought the error was at this.

models/user.py

@staticmethod
def jwt_handler(token):
    if not User.objects(token=token):
        raise JWTError("Bad bad bad bad")

    secret = str(current_app.config["JWT_SECRET_KEY"])
    algorithm = str(current_app.config["JWT_ALGORITHM"])
    options = {
        'verify_' + claim: True
        for claim in verify_claims
    }

    options.update({
        'require_' + claim: True
        for claim in required_claims
    })

    decode = jwt.decode(token, secret, options=options, algorithms=[algorithm])
    return decode

@staticmethod
def authenticate(username, password):
    user = User.objects(username=username)
    if len(user) == 0:
        return None
    user = user[0]
    user["id"] = str(user["id"])

    if crypt.verify(password, user.password):
        return user

    return user

routes/user.py

def auth():
    username = request.form.get("username")
    password = request.form.get("password")

    if not username:
        raise BadRequest("Userna doesn't exists")

    user = user_ctrl.read(username)

    identity = _jwt.authentication_callback(username, password)

    if identity:
        access_token = _jwt.jwt_encode_callback(identity)
        identity.update(push__token=access.decode("utf8"))
        return _jwt.auth_response_callback(access_token, identity)
    else:
        raise JWTError("Bad bad bad very bad")

config.py

import os
from test.models import db

class Config(object):

    db_name = os.getenv('MONGODB_NAME', 'third')
    db_host = os.getenv('MONGODB_HOST', '127.0.0.1')
    db_port  = os.getenv('MONGODB_PORT', '5000')

    JWT_SECRET_KEY = 'test123'
    JWT_ALGORITHM = 'SH256'
    JWT_AUTH_ENDPOINT = 'jwt'
    JWT_AUTH_USERNAME_KEY = 'username'
    JWT_AUTH_PASSWORD_KEY = 'password'

http.py

import logging.config
import jwt
from flask_jwt import JWT
from flask import Flask

from test import routes
from test.models import db, User

_jwt = JWT(authentication_handler=User.authenticate, identity_handler=User.identity)
_jwt.jwt_decode_callback=User.jwt_handler

def create_app(config):
    app = Flask(__name__.split(',')[0])

    app.register_blueprint(routes.user.bp)
    app.register_blueprint(routes.auth.bp)

    db.init_app(app)

    _jwt.init_app(app)

    return app
15
  • The traceback says the error is at File "/home/def/pr/flask/flask_deeper/test/routes/auth.py", line 26 Can you post the code present there? Commented Nov 23, 2016 at 4:51
  • this the code on line 26. access_token = _jwt.jwt_encode_callback(identity) Commented Nov 23, 2016 at 4:57
  • No I meant the whole code block Commented Nov 23, 2016 at 5:38
  • <code>@bp.route("/auth", methods=["POST"]) def auth(): username = request.form.get("username") password = request.form.get("password") if not username: raise BadRequest("Userna doesn't exists") user = user_ctrl.read(username) identity = _jwt.authentication_callback(username, password) if identity: access_token = _jwt.jwt_encode_callback(identity) # identity.update(push__token=access.decode("utf8")) return _jwt.auth_response_callback(access_token, identity) else: raise JWTError("Bad bad bad very bad" </code> Commented Nov 23, 2016 at 5:52
  • You can "edit" your question and add the code block there (The link to edit is at the end of the question to the left hand side). It is difficult to understand code pasted in comments. Commented Nov 23, 2016 at 5:53

1 Answer 1

2

You have defined the configuration is config.py but have not added the configuration object to your flask app. Therefore, keys such as JWT_SECRET_KEY are not in your app config.

Flask-JWT's default_handler expects those values (Copied in case source changes)

def _default_jwt_decode_handler(token):
    secret = current_app.config['JWT_SECRET_KEY']
    algorithm = current_app.config['JWT_ALGORITHM']
    leeway = current_app.config['JWT_LEEWAY']

In your case as that is not set, it returns None and trips the algorithms.py (which expects a string key).

Therefore, during your app initialization in http.py, you must add a call to app.config.from_object. Maybe something like this

def create_app(config):
    app = Flask(__name__.split(',')[0])

    # Edit the following to point it to your Config class
    app.config.from_object(config.Config)

    app.register_blueprint(routes.user.bp)
    app.register_blueprint(routes.auth.bp)

    db.init_app(app)

    _jwt.init_app(app)

    return app

On a side note, the name of JWT_ALGORITHM should be HS256 rather than SH256 (Although it doesn't matter as HS256 is the default and will be chosen since SH256 is not a valid algorithm)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.