Error in course Building Neo4j Applications with Python

for this course
Building Neo4j Applications with Python | Free Neo4j Courses from GraphAcademy

following steps of the course,
in the step

Registering a User | GraphAcademy (neo4j.com)

When I run application to register a new user, I am getting this error for JWT token

127.0.0.1 - - [12/Apr/2023 18:45:01] "POST /api/auth/register HTTP/1.1" 500 -
Traceback (most recent call last):
File "C:\Users\z004mdvc.conda\envs\neoflix\lib\site-packages\flask\app.py", line 2548, in call
return self.wsgi_app(environ, start_response)
File "C:\Users\z004mdvc.conda\envs\neoflix\lib\site-packages\flask\app.py", line 2528, in wsgi_app
response = self.handle_exception(e)
File "C:\Users\z004mdvc.conda\envs\neoflix\lib\site-packages\flask_cors\extension.py", line 165, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "C:\Users\z004mdvc.conda\envs\neoflix\lib\site-packages\flask\app.py", line 2525, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\z004mdvc.conda\envs\neoflix\lib\site-packages\flask\app.py", line 1822, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\z004mdvc.conda\envs\neoflix\lib\site-packages\flask_cors\extension.py", line 165, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "C:\Users\z004mdvc.conda\envs\neoflix\lib\site-packages\flask\app.py", line 1820, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\z004mdvc.conda\envs\neoflix\lib\site-packages\flask\app.py", line 1796, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
File "C:\Users\z004mdvc.conda\envs\neoflix\app-python-main\app-python-main\api\routes\auth.py", line 18, in register
user = dao.register(email, password, name)
File "C:\Users\z004mdvc.conda\envs\neoflix\app-python-main\app-python-main\api\dao\auth.py", line 59, in register
payload["token"] = self._generate_token(payload)
File "C:\Users\z004mdvc.conda\envs\neoflix\app-python-main\app-python-main\api\dao\auth.py", line 179, in _generate_token
return jwt.encode(
File "C:\Users\z004mdvc.conda\envs\neoflix\lib\site-packages\jwt\api_jwt.py", line 67, in encode
return api_jws.encode(json_payload, key, algorithm, headers, json_encoder)
File "C:\Users\z004mdvc.conda\envs\neoflix\lib\site-packages\jwt\api_jws.py", line 153, in encode
key = alg_obj.prepare_key(key)
File "C:\Users\z004mdvc.conda\envs\neoflix\lib\site-packages\jwt\algorithms.py", line 186, in prepare_key
key = force_bytes(key)
File "C:\Users\z004mdvc.conda\envs\neoflix\lib\site-packages\jwt\utils.py", line 22, in force_bytes
raise TypeError("Expected a string value")
TypeError: Expected a string value

@shsamardar Can you confirm lines 92-104 in your /api/dao/auth.py file looks like this?

There used to be an errant decode('ascii') at the end of the jet.encode() function that caused issues. The latest commit from Feb 9 should have had this removed, so I'm wondering if you're on an older commit?

i uploaded the code 3 days ago

this is my code

def _generate_token(self, payload):
iat = datetime.utcnow()

    payload["sub"] = payload["userId"]
    payload["iat"] = iat
    payload["nbf"] = iat
    payload["exp"] = iat + current_app.config.get('JWT_EXPIRATION_DELTA')
    
    return jwt.encode(
        payload,
        self.jwt_secret,
        algorithm='HS256'
    )
# end::gene

sorry, i meant downloaded the code 3 days ago

i have spent 4 hours yesterday to solve this puzzle, still no luck

is there any insight you can provide here?

Apologies for the slow reply @shsamardar. An update was pushed up to the Gitpod instance last Friday. Give it another go with that update.

Hello

I have all of my code on my laptop. and I dont know how I got them through what step in the course.

I have a python enviorment called neoflix and i have folder called app_python_main

I didn't use Gitpod before

if i want to get the new changes, how can i do it through steps i mentioned in above.

i can't find steps i did when i started the course ...

thanks

can you please help me in here to have some progress for resolution

Can you upload your repository to github so I can see what is happening? It is also worth checking that you have all of the information set in your .env file.

If you re-open the lesson in graphacademy, you should now see an Open challenge in Gitpod button which will load the repository with any environment variables in place.

Let me know if that solves the problem for you.

Hello Adam,

Is there a repository to solution of python course, specifically to files in router and dao ?
I went through course via flask application on my laptop but somehow i lost all of the changes..
I guess i overwrote them with another import from repository .

thanks

Each lesson should give you instructions to check out a branch with working code for that code. The code should also be in the lesson. Let me know if anything is missing.

i checked out branch 15-peron-profile

assuming this branch has all of the code changes for the course.

i registered a user, i see node is created. I logged in to that user

When i logged in as a registered user, suddenly everything is empty , even browse Genre shows nothing or popular movies or latest releases

is that correct behavior?

thanks

That seems like a bug in the UI. Could you email me a video of what you're doing so I can double-check (graphacademy at neo4j dot com)?

The white screen won't affect you passing the course. It only relies on the test running correctly.

I was receiving the same error message when running the test script.

======================================================================================== short test summary info ========================================================================================
FAILED tests/03_registering_a_user__test.py::test_register_user - TypeError: Expected a string value

I figured out the issue (with some AI help from Google Bard).

This should be the code for the _generate_token function:

def _generate_token(self, payload):
    iat = datetime.utcnow()

    payload["sub"] = payload["userId"]
    payload["iat"] = iat
    payload["nbf"] = iat
    payload["exp"] = iat + current_app.config.get('JWT_EXPIRATION_DELTA')

    key = str(self.jwt_secret) #<-----ADD THIS LINE

    return jwt.encode(
        payload,
        key, #<------CHANGE THIS LINE
        algorithm='HS256'
    )

This now passes returns a pass after running the test script. Hope that works.

2 Likes

John

thank you for passing information

in your setup

When you logged in as a "registered user" ( after user registration) , do you still see movies or Genres ?

for me, suddenly everything is empty , even browse Genre shows nothing or popular movies or latest releases

This sounds like a problem loading data from the API. Do you see 500 errors in the console or in Developer Tools > Network?

I don't understand what you are asking

which UI are you referring to?

Hi there,

I've just run into this problem also - I got the same error message. But I was able to verify via the sandbox that the new user node was created.

Here's my auth.py code:

import bcrypt
import jwt
from datetime import datetime

from flask import current_app

from api.exceptions.badrequest import BadRequestException
from api.exceptions.validation import ValidationException

from neo4j.exceptions import ConstraintError

class AuthDAO:
"""
The constructor expects an instance of the Neo4j Driver, which will be
used to interact with Neo4j.
"""
def init(self, driver, jwt_secret):
self.driver = driver
self.jwt_secret = jwt_secret

"""
This method should create a new User node in the database with the email and name
provided, along with an encrypted version of the password and a `userId` property
generated by the server.

The properties also be used to generate a JWT `token` which should be included
with the returned user.
"""
# tag::register[]
def register(self, email, plain_password, name):
    encrypted = bcrypt.hashpw(plain_password.encode("utf8"), bcrypt.gensalt()).decode('utf8')

    def create_user(tx, email, encrypted, name):
        return tx.run(""" // (1)
            CREATE (u:User {
                userId: randomUuid(),
                email: $email,
                password: $encrypted,
                name: $name
            })
            RETURN u
        """,
        email=email, encrypted=encrypted, name=name # (2)
        ).single() # (3)

    try:
        with self.driver.session() as session:
            result = session.execute_write(create_user, email, encrypted, name)

            user = result['u']

            payload = {
                "userId": user["userId"],
                "email":  user["email"],
                "name":  user["name"],
            }

            payload["token"] = self._generate_token(payload)

            return payload
    except ConstraintError as err:
        # Pass error details through to a ValidationException
        raise ValidationException(err.message, {
            "email": err.message
        })
# end::register[]

"""
This method should attempt to find a user by the email address provided
and attempt to verify the password.

If a user is not found or the passwords do not match, a `false` value should
be returned.  Otherwise, the users properties should be returned along with
an encoded JWT token with a set of 'claims'.

{
  userId: 'some-random-uuid',
  email: 'graphacademy@neo4j.com',
  name: 'GraphAcademy User',
  token: '...'
}
"""
# tag::authenticate[]
def authenticate(self, email, plain_password):
    # TODO: Implement Login functionality
    if email == "graphacademy@neo4j.com" and plain_password == "letmein":
        # Build a set of claims
        payload = {
            "userId": "00000000-0000-0000-0000-000000000000",
            "email": email,
            "name": "GraphAcademy User",
        }

        # Generate Token
        payload["token"] = self._generate_token(payload)

        return payload
    else:
        return False
# end::authenticate[]

"""
This method should take the claims encoded into a JWT token and return
the information needed to authenticate this user against the database.
"""
# tag::generate[]
def _generate_token(self, payload):
    iat = datetime.utcnow()

    payload["sub"] = payload["userId"]
    payload["iat"] = iat
    payload["nbf"] = iat
    payload["exp"] = iat + current_app.config.get('JWT_EXPIRATION_DELTA')

    return jwt.encode(
        payload,
        self.jwt_secret,
        algorithm='HS256'
    )
# end::generate[]

"""
This method will attemp to decode a JWT token
"""
# tag::decode[]
def decode_token(auth_token, jwt_secret):
    try:
        payload = jwt.decode(auth_token, jwt_secret)
        return payload
    except jwt.ExpiredSignatureError:
        return None
    except jwt.InvalidTokenError:
        return None
# end::decode[]

Here's the terminal:
pod /workspace/app-python (main) $ pytest tests/03_registering_a_user__test.py
=============================================================== test session starts ================================================================
platform linux -- Python 3.11.9, pytest-7.1.3, pluggy-1.0.0
rootdir: /workspace/app-python
collected 1 item

tests/03_registering_a_user__test.py F [100%]

===================================================================== FAILURES =====================================================================
________________________________________________________________ test_register_user ________________________________________________________________

app = <Flask 'api'>

def test_register_user(app):
    with app.app_context():
        driver = get_driver()

        dao = AuthDAO(driver, os.environ.get('JWT_SECRET'))
      user = dao.register(email, password, name)

tests/03_registering_a_user__test.py:31:


api/dao/auth.py:58: in register
payload["token"] = self._generate_token(payload)
api/dao/auth.py:115: in _generate_token
return jwt.encode(
../.pyenv_mirror/user/3.11.9/lib/python3.11/site-packages/jwt/api_jwt.py:67: in encode
return api_jws.encode(json_payload, key, algorithm, headers, json_encoder)
../.pyenv_mirror/user/3.11.9/lib/python3.11/site-packages/jwt/api_jws.py:153: in encode
key = alg_obj.prepare_key(key)
../.pyenv_mirror/user/3.11.9/lib/python3.11/site-packages/jwt/algorithms.py:186: in prepare_key
key = force_bytes(key)


value = None

def force_bytes(value: Union[str, bytes]) -> bytes:
    if isinstance(value, str):
        return value.encode("utf-8")
    elif isinstance(value, bytes):
        return value
    else:
      raise TypeError("Expected a string value")

E TypeError: Expected a string value

../.pyenv_mirror/user/3.11.9/lib/python3.11/site-packages/jwt/utils.py:22: TypeError
============================================================= short test summary info ==============================================================
FAILED tests/03_registering_a_user__test.py::test_register_user - TypeError: Expected a string value
================================================================ 1 failed in 1.24s =================================================================
gitpod /workspace/app-python (main) $

VS Code is telling me that the method .utcnow() in the datetime class is deprecated.

Hello. I'm facing with a similar error.

I updated auth.py with the exact same working solution from Building Neo4j Applications with Python - Registering a User. However, when running tests/03_registering_a_user__test.py, it returns an error.--> TypeError: Expected a string value
This mainly stem from -->
jwt.encode() function is receiving a None value for the key parameter, which is causing the TypeError: Expected a string value.

Can anybody help me with this, please?