Search code examples
jwtweb2py

Auth with JWT in web2py


I run with web2py a rest api in the controller and there my jwt and the token works. In the model I am using fields with default values but this fields are never filled automatically because auth.user is None. I tried to init it manually but it does not work.

Sites.py:

if auth.user is None:
    auth = Auth(db, jwt = {'secret_key':'..hnv', 'user_param':"email"})
    #auth.jwt()
    auth.basic()
    #auth.user.update()

I tried also to add the parameter in db.py but no success:

auth = Auth(db,jwt = {'secret_key':'...', 'user_param':"email"}, host_names=configuration.get('host.names'))

Does I expect too much or I am doing something wrong?


Solution

  • auth = Auth(db, jwt = {'secret_key':'...', 'user_param':"email"})
    

    If you instatiate Auth as above (i.e., adding the JWT functionality), when a request with a JWT token comes in, auth.user is not immediately populated when the above line is executed. Rather, the token is only read and auth.user populated when the @auth.allows_jwt() decorator is executed (i.e., in the requested controller). This means that in your model files where you have your DAL table definitions, auth.user (and auth.user_id and auth.user_groups) will still be None during requests authorized via JWT, so you cannot directly use those values as field defaults.

    As an alternative, you can instead specify a function as a field default, with the function returning the relevant auth property:

    db.define_table('mytable',
        ...,
        Field('created_by', default=lambda: auth.user_id))
    

    The lambda function defined above will not be called when the table is defined, but only later when actually creating a record, which presumably will only happen in a controller that has been decorated with @auth.allows_jwt() (and therefore, auth.user will have been populated by that point).

    Another option is to force auth.user to be populated in the model file by calling auth.allows_jwt there (it returns a decorator, but since we are not using it as a decorator, we must instead pass it a no-op function and then call the returned function):

    auth = Auth(db, jwt = {'secret_key':'...', 'user_param':"email"})
    if not auth.user:
        try:
            # If there is a token, this will populate `auth.user`, 
            # `auth.user_id`, and `auth.user_groups` from the token data.
            auth.allows_jwt()(lambda: None)()
        except:
            # An error means the token is absent, expired, or invalid.
            pass
    

    Once the above has been executed, you can then use auth.user and auth.user_id to specify field defaults in model definitions.