Force a user to be logged out, even if they havve an active session, after a certain amount of time

What I’m trying to do:
Force logout of a user from the server after a certain length of time, even if they have an active session
What I’ve tried and what’s not working:
Using a Timer on a form works great, but I need this to be from server code. I tried doing it from a background task that checks periodically, but since the background task runs in its own session, it can’t log the user out of the app, it just logs the user out of the session it runs in (which is different from the real user session, as described here: Users.get_user() returns None in freshly launched background tasks - #6)

I could put the check at the beginning of every anvil.server.callable, but we have hundreds of those. I’m hoping for another option.

I seem to recall someone wrapping anvil.server.call and anvil.server.callable for batching server calls. Perhaps a similar technique…?

1 Like

Unfortunately, I don’t think it’s possible to forcefully log out a user from the server side in the way you’re describing. Users aren’t actually “logged in” on the server, they’re authenticated in the client, and the server only trusts what the client tells it.

For example, you could have two browser sessions: one logged in, one logged out. When a request comes in from the logged-in session, the server sees the valid session token and treats the user as authenticated. When the request comes from the other session, it doesn’t, but this behavior is entirely driven by the client’s state.

So, forcing a logout from the server isn’t really feasible because there’s nothing to “log out”. The client just stops sending the session token when it considers the user logged out.

That said, a few possible approaches:

  • Client-side timeout: Have the client manage a timer and log the user out after a set period of inactivity.
  • Server-side checks: Store a per-user or per-session timestamp on the server, and reject requests (or ignore tokens) once the time limit has passed.
  • Session tracking: You could implement your own session store with timestamps and periodic cleanup, giving you more control over how long sessions are considered valid.

Or maybe I misunderstood your use case.

Thanks! I believe you understand the use case.

Client-side timeout. This works great, but I can’t trust it (since it is on the client side). While I agree that the chance of someone bothering to fiddle with it in the browser is small, because of the type of data we are storing, it just isn’t acceptable.

Server-side checks. This seems to be the only reasonable way to do it. I just did not want to add that to hundreds of places in our application. Even just adding an additional decorator is a lot of changes.

Our own Session store. Not sure how we would do this within anvil, but it sounds interesting. Is there an example you know of you could point me to? I scanned the forum but nothing popped out at me.

I think I’m going to move forward with adding server-side checks everywhere. Not ideal, but I think it will work.

I’d go through all the @anvil.server.callable functions and insert the check there.

Doing this from the online IDE can be tedious, so I usually clone all my apps locally and use PyCharm for this kind of task.

Sometimes you get lucky and can handle it with a global regex replace. Other times, I select the first @anvil.server.callable in a file, press Alt+J repeatedly to multi-select all occurrences, then move the cursor down and insert the code once — it gets applied to all selections. After that, I review the diffs to confirm the changes before committing. With PyCharm, the whole process only takes a few minutes.

Yeah, I always assumed it would be as simple as modifying the callable decorator to include a check function: (this code is untested, just an example)

from datetime import datetime as dt
from datetime import timedelta
from anvil.tz import tzutc

def time_exceeded(user):
    max_hours = 5
    if (dt.now(tzutc()) - user['last_login']) < timedelta(hours=max_hours):
        return True
        
    user.logout(invalidate_client_objects=True)
    return False
    
    
@anvil.server.callable(require_user=time_exceeded)
def every_callable_function_to_be_decorated():
    ...
    

2 Likes

This is a clean and more elegant solution.

That said, I’d suggest one addition: whether you use a decorator or any other mechanism on the server side, it can control what gets returned for logged-in or logged-out users — but that alone might not be enough.

The client also needs to be aware that the server has invalidated the session. So, after every server call from the client, you should include a check to determine whether the user should be logged out.

One approach is to have the server callable return a tuple like (result, must_log_out). Another is to raise a custom SessionExpired exception. In either case, the client should handle the return value or exception appropriately and trigger a logout if needed — for every server call.

I’d be inclined towards the custom Exception approach, with a global handler. But occasionally a wrapper around anvil.server.call() fits better. Both can work together.