I would like to ‘force logout’ a user.
anvil.users.logout()
acts on the CURRENT user.
Is there a way to force logout a differnet user? I.e.:
anvil.users.force_logout(some_user_row)
Thanks!
I would like to ‘force logout’ a user.
anvil.users.logout()
acts on the CURRENT user.
Is there a way to force logout a differnet user? I.e.:
anvil.users.force_logout(some_user_row)
Thanks!
Remember, someone can be logged in from a dozen different browser tabs simultaneously, from anywhere in the world. Each counts as an actively-logged-in session, so each session would have to be logged out individually.
So this will have to be triggered by your browser-side code, checking periodically for a “reason” (e.g., a database record) to log itself out. If that calls server-side code to do the checking, then your server code can reliably log them out.
If nothing calls the Server (incl. db access) for 30 minutes, then they’re automatically logged out.
Thanks @p.colbert.
There is knowledge server-side of all logged in sessions. So invalidating all those sessions server-side should be possible. To your point, calling force_logout
would need to invalidate ALL sessions the user may have.
There are many good reasons we would want to invalidate a user’s session, and relying on users to do it client-side would not be practical or possible (what if you identify that a user has been highjacked and need them disabled and out?).
I don’t rely on the user to do it. I rely on a Timer component, instead: invisible, and not under (normal) user control.
I do a similar check at log-in time, so that in a browser tab where they’re currently not logged in, they can’t become logged-in.
Anvil’s infrastructure certainly has such information, but I am not aware of any central point of access or control available to developers like us. This might make a good Feature Request.
I just looked into the server runtime, if you force reset a users password from inside a server module, it looks like anvil has a clojure function that finds all instances of a user id and invalidates all their logins.
But you would need to reset the users password, so that’s kind of drastic.
That is cool
You can get the session ID, so maybe keep that and kill the session? Will that force the logout?
If the use case is a compromised account, resetting the user password is the right thing to do anyway. But I can imagine another scenario, like needing to end all current sessions to force an app update, for which password resets would be inappropriate.
I just re-read my own reply from last night and thought of a workaround, but it’s quite round-about and complicated.
I will write some code and test it, and then start a FR requesting that the clojure method (if it works) is exposed to a more usable python call.
Stand by…
also:
This looks to my untrained eye that this is what the clojure method does, gathers all the sessions attached to the user id and invalidates that they are logged in. It does not kill them though just makes them no longer logged in like with a force_logout()
.
References:
And here is what that leads to:
Ok, so I successfully triggered the reset_password
function in the users service by fiddling with password hashes and the users
table.
It does not seem to work. Either I am testing it wrong, or it does not do what I think it does, or both.
It runs fine with no errors, but it does not seem to have any effect on other logged in sessions or the session it was called from.
This is what I tried, if someone else wants to give it a go:
import bcrypt
def make_new_user_pass(input_password):
hashed = bcrypt.hashpw(input_password.encode(), bcrypt.gensalt())
# print(hashed)
return hashed.decode('utf-8')
def reset_test():
user_row = anvil.users.get_user()
old_hash = str(user_row['password_hash']) #dereference
user_row['password_hash'] = make_new_user_pass("Someresetpassword1234")
anvil.users.force_login(user_row)
try:
from anvil.users import reset_password
reset_password( "Someresetpassword1234",
"Someresetpassword12345" #different
)
except Exception as err:
user_row['password_hash'] = old_hash
raise
user_row['password_hash'] = old_hash
print("Forced to log out")