auth.get_user auth.login always returns a truthy value

93 views
Skip to first unread message

Alfonso Serra

unread,
Jun 1, 2025, 10:57:12 PM6/1/25
to py4web
In web2py auth.user returned None which made easy to check for invalid credentials

In py4web, auth.get_user, auth.login returns an empty object which is a truthy value  for an invalid user

Statements like:

if auth.login(email, password):
    ....show authenticated content

if auth.get_user:
     ....show authenticated content

would allow unauthorized users to see restricted content.

The way we have left to check whether the authentication was successful is checking whether the returned object has any key which may be unreliable.

The bottom line is that is not a good idea to return truthy values for unauthorized login attempts. auth.get_user at auth.py line 526. It would be great if this could be improved. Thanks!

Ali

unread,
Jun 1, 2025, 11:03:10 PM6/1/25
to py4web
I use auth.current_user, which consistently returns a falsy value when there is no logged-in user. Please let me know if more information is needed.

Massimo DiPierro

unread,
Jun 1, 2025, 11:05:24 PM6/1/25
to Ali, py4web

The simplest way is auth.user_id != None with.current_user has DB overhead


--
You received this message because you are subscribed to the Google Groups "py4web" group.
To unsubscribe from this group and stop receiving emails from it, send an email to py4web+un...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/py4web/de6e01b0-de45-4e5e-a3ff-b9276a71a1cen%40googlegroups.com.

Alfonso Serra

unread,
Jun 2, 2025, 1:55:42 PM6/2/25
to py4web
Thanks for the tips. Ill use any of these alternatives.

Julio Flores Schwarzbeck

unread,
Jun 6, 2025, 1:47:51 PM6/6/25
to py4web
I too, use a heavily modified version of authentication (mostly due to UI and workflow design choices), while =FORM() is incredibly useful for many situations, I don't use it in any of my applications.

I am therefore at the mercy of the internal py4web mechanism to sign up/login, here's my example (edited for simplicity):


### Custom Login
@action('custom_auth/login', method=['get', 'post'])
@action.uses('custom_auth/login.html', auth)
def custom_auth_login():
    """ Custom Login Page """
    if request.method == 'POST':
        email = request.forms.get('email', '')
        passwd = request.forms.get('passwd', '')
       
        # internal engine's login
        user, error = auth.login(email, passwd)
       
        if user:
            auth.store_user_in_session(user['id'])
           
           
 ### Custom Registration           
@action('custom_auth/register', method=['get', 'post'])
@action.uses('custom_auth/register.html')
def custom_auth_register():
    """ Custom Register Page """
    if request.method == 'POST':
        email = request.forms.get('email', '')
        password = form.get('password', '')
        password_again = form.get('password_again', '')

        crypt_password = CRYPT()(password)[0]
        payload = {
            'username': 'testuser',
            'email': email,
            'password': crypt_password,
            'password_again': crypt_password,
            'first_name': 'First;,
            'last_name': 'Last'
        }

        # Internal engine's register
        registration_results = auth.register(payload, validate=False)
        # if 'errors' in registration_results, then handle them


I would like to see an interface that provides these methods "naked", that is, decoupled from FORM() or other elements, maybe the auth REST endpoints could do this, however the above is working perfectly fine for my needs, however, if any of the implementation details in auth.py change, (say, py4web removes or replaces CRYPT(), then the above will need to be revised).

As always thanks Massimo for this awesome framework. 

On Sunday, June 1, 2025 at 7:57:12 PM UTC-7 aleon...@gmail.com wrote:

Alfonso Serra

unread,
Jun 12, 2025, 6:14:29 AM6/12/25
to py4web
Thanks for the answer Julio and very good points. 
At some time i would have to do the same. Im designing a private website (mandatory login to see any content) that wont use /login or /register urls, all these actions will be done at the root url / so i wont be able to use standard decorators or actions.

I agree that the framework should not enforce specific routes to provide this functionality. 

Cheers!

Massimo

unread,
Jun 14, 2025, 2:29:07 AM6/14/25
to py4web
In common.py there is this line
auth.enable(uses=(session, T, db), env=dict(T=T))

this is what exposes the actions.
One thing you can do is replace /auth/ with anything you want like /myauth/

auth.enable(route="myauth", uses=(session, T, db), env=dict(T=T))

Another think if you can do is declare this is a single page app so that forms routes are not defined for you

auth.enable(uses=(session, db), spa=True)

and declare them yourself

@action("my-login-page")
@action.uses(auth)
def _():
     return dict(form =auth.form("login"))

@action("my-register-page")
@action.uses(auth)
def _():
     return dict(form =auth.form("register"))

Or use the form API if you have client side forms.
Reply all
Reply to author
Forward
0 new messages