The problem is when user tries 'forgot password' option. It creates new reset_key for verification, but the new key is not getting updated into DB.
#app.route('/login/forgot/', methods=['GET', 'POST'])
def forgot():
form = ResetLoginForm(request.form)
#There's no session yet. User just pointing to /login/forgot url.
if request.method == 'POST' and form.validate():
user = User.query.filter_by(email=form.email.data).first()
if not user:
flash('The username or email incorrect')
return render_template('forgot.html', form=form)
reset_key = generate_key() ## this creates a new key, but how update this key into db?
#tried something like
user.reset_key = reset_key
db.session.add(user)
db.session.commit()
#this is not working. Is it due to session is not started or something?
Thanks for any help or hint.
This is because User.query.filter_by(email=form.email.data).first() will return a sqlalchemy.orm.query.Query object. As its doc says:
Query is the source of all SELECT statements generated by the ORM,
both those formulated by end-user query operations as well as by high
level internal operations such as related collection loading. It
features a generative interface whereby successive calls return a new
Query object, a copy of the former with additional criteria and
options associated with it.
So you just get a copied object, so your change will not work;
You can use like this:
user = db.session.query(User).filter_by(email==form.email.data).first()
and then you can change user attrs
user = db.session.query(User).first() solved problem.
Related
The below login solution is not successfully authenticating the user.
After the login_user(user) function is called, the current_user is supposed to be authenticated.
When trying: print(flask_login.current_user.is_authenticated) immediately after login this returns TRUE, but as soon as the next page loads it returns FALSE.
In summary: The user authentification state is not persistent.
Consequently, pages protected with the #flask_login.login_required decorator (below the #app.route line) are inaccessible and I am redirected to the #login_manager.unauthorized_handler page.
As I understand it, the Flask framework is supposed to set cookies or send some kind of information in the headers that contain some kind of authentication. I also tried using the flask 'sessions' module and also tried using the 'g' module, but these produced entirely different errors.
The below version of the code I plagiarised from https://github.com/maxcountryman/flask-login, which uses only the things I have below. Yet for some reason, my version isn't working.
For the purposes of this testing phase, I made a list containing python dicts of a user so I don't have to make multiple unnecessary calls to the Firebase db I'm using to store the info.
This is the setup section for the login manager including my copy of the UserMixin class:
app.secret_key = 'dummysecretkey'
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
class User(flask_login.UserMixin):
pass
This is a sample of the structure of the users:
{
'email': 'email#domain.com',
'password': 'password',
'username': 'user01'
...*other fields*...
}
Here are the routes for the login manager:
#login_manager.user_loader
def user_loader(username):
for entries in users:
if entries['username'] not in users:
return
for entries in users:
if entries['username'] == username:
user = User()
user.id = entries['username']
return user
#login_manager.request_loader
def request_loader(request):
username = request.form.get('username')
try:
for entries in users:
if entries['username'] == username:
user = User()
user.id = entries['username']
user.is_authenticated = request.form['password'] == entries['password']
return user
except:
return None
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
uname = request.form['uname']
psw = request.form['psw']
for entries in users:
if uname == entries['username'] and psw == entries['password']:
user = User()
user.id = uname
flask_login.login_user(user)
print(flask_login.current_user.is_authenticated)
return redirect(url_for('addnew'))
return 'Bad login'
Example route that requires authentication:
#app.route('/addnew')
#flask_login.login_required
def addnew():
return render_template("addnew.html")
Notwithstanding the flaws in the password comparison (I'm aware they're insecure), could someone please point out the flaw in the above code or present an alternative to achieve the login authentication?
I am bit confused by your code.
From the Flask Login documentation:
Sometimes you want to login users without using cookies, such as using header values or an api key passed as a query argument. In these cases, you should use the request_loader callback.
But I understand you want to use cookie based login?
So you should not use the request_loader.
Also, why do you instantiate a new User in the login route? The login should compare the login data with already existing users.
I suggest you "manually" instantiate the users and put them in a list like this:
users = []
a = User()
a.name = "name_a"
a.id = "id_a"
users.append(a)
...
And then adapt your user_loader to compare the login data from the form with the existing data.
There is an excellent Flask tutorial out there by Miguel Grinberg, which also explains the user login:
https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world
I know this is a possible duplicate post, I found a solution for this, but those posts are from older versions of Django and I can't get the new syntaxis. Could someone give me a tip please?
I'm trying to prevent users to use the same account simultaneously. So when someone log in an account, if the user and pass are ok, I'm iterating the django_session table in order to find if theres any other session with the same account, if yes, close this session and start the last one.
After auth.authenticate(blah blah):
if user is not None:
# Checks if there's any other active session with the same account
for session in Session.objects.filter(session_key=request.session.session_key): # //aq
data = session.get_decoded()
print(data.get('_auth_user_id', None))
if data.get('_auth_user_id', None) == str(user.id):
session.delete()
But something is not working with this syntaxis.
I can't get the session user's ID.
When a user logs in I'm using request.session['logueado'] = user.id and auth.login(request, user).
This should be like a "last to log in wins" system.
EDIT: Whole code:
def login(request):
if 'logged' in request.session:
return redirect(main)
if request.method == "GET":
form = LoginForm
return render(request, "login.html", {"form": form})
if request.method == "POST":
print('POST method.')
form = LoginForm(request.POST)
if form.is_valid():
print('Valid form.')
user_name = form.cleaned_data.get('username')
user_pass = form.cleaned_data.get('password')
user = auth.authenticate(username=user_name, password=user_pass)
if user is not None:
for session in Session.objects.filter(session_key=request.session.session_key):
print('Hello')
data = session.get_decoded()
if data.get('_auth_user_id', None) == request.session.session_key:
print('Same account in more than 1 session.')
session.delete()
#------------------------------------------------------------------
request.session['logged'] = user.id
print(request.session['logged'])
auth.login(request, user)
request.session.set_expiry(0) #
return redirect(main)
else:
return render(request, "loginError.html", {"form": form})
EDIT: Definitive solution: (Thank you Daniel Roseman!)
user = auth.authenticate(username=user_name, password=user_pass)
if user is not None:
# Checks if there's another session with the same acc
Sessions = Session.objects.all()
for row in Sessions:
print(row.get_decoded().get('_auth_user_id'))
print(user.id)
if str(row.get_decoded().get("_auth_user_id")) == str(user.id):
print('Same sessions')
row.delete()
request.session['logged'] = user.id
Your code is based on all sorts of false assumptions.
Session keys don't in any way map to login IDs. And they are unique: each client will have a different key, even if they're logged in as the same user. What's more, the session is explicitly flushed when a user logs in, so they will get a new key.
In other words, the only way to do something like this would be to iterate over all sessions in the database, decode them, and check if their _auth_user_id item is equal to the user ID.
As Daniel Roseman answered, you can iterate over all sessions in DB, decode all of them, and delete those you want. But it's slow, particularly if your site has high traffic and there are lots of sessions.
If you need a faster solution, you can use a session backend that lets you query and get the sessions of a specific user. In these session backends, Session has a foreign key to User, so you don't need to iterate over all session objects:
django-user-sessions (based on django's db session backend)
django-qsessions (based on django's cached_db session backend)
Using these backends, deleting all sessions of a user can be done in a single line of code:
user.session_set.all().delete()
Disclaimer: I am the author of django-qsessions.
I have my first project as junior in my work. It is old (Django 1.8) and it is normal django framework... not REST.
It supports web, and mobile.
I have to create endpoint for mobile to create user.
I think it is not a problem (to create) but I want to make sure it will be save.
First of all I thought that I will create normal ModelForm (RegisterAPIForm based on model=User with full validation (I mean init all fields that are in "backend" not visible for user | cleaned_data for all fields | special overwriten method save() that in addition hashes password, and send email) and in Views I'll add something like this:
class RegistrationAPITestView(View):
def post(self, request):
form = RegistrationAPIForm(
request.POST
)
if form.is_valid():
form.save()
return JsonResponse({})
else:
#check errors and send error code back
Or I should do it other way, by using User object?
class RegistrationAPITestView(View):
def post(self, request):
#check if user does not exist
#password1 and password2 validation
user = User.objects.create()
user.name = request.POST['username']
user.set_password(request.POST['password'])
#init all fields that user can't choose like groups etc
user.save()
What do you think? Do I need ModelForm that I won't even render? It seems to be safer, but maybe I should check it other way? with User object?
Btw. Registration form already exists for web but there is a lot of "web" stuff that I don't need and I don't have to check and there is another method of saving password, so I believe I should create new one.
My code will be revieved in 2 weeks (senior vacations) but now I'm alone and want to do my best.
There is nothing wrong with the second option, but here is the problem that you as a junior should avoid. This line will make the server return a 500 error request.POST['username'] because python will throw a key error if the user doesn't provide the username, to fix just change to request.POST.get('username', 'value if doesn\'t exit') also make sure that everything is ready before create the user or you will have records in the database that wont be useful. Call validators to the password too and try to cover all possible scenario. Remember never trust the user
I have the following code:
# creating user:
def create_user(request):
if request.method == 'POST':
user_info = forms.UserInfoForm(request.POST)
if user_info.is_valid():
cleaned_info = user_info.cleaned_data
User.objects.create_user(username=cleaned_info['username'], password=cleaned_info['password'])
render(.......)
This works. I can check the auth_user and I see the username and password along with all the other fields created and added.
Now, I try to authenticate the user with the following code after creating user with username='testcase' and password='test': using above code.
# Authenticate User
def get_entry(request):
if request.method == 'POST':
user = authenticate(username='testcase', password='test')
if user:
.........
The user is always returned as none. What is going on? I am running django 1.10.2.
Update:
I can see the user created by create_user function when I log in admin. The status was not staff(as it was supposed to be). I changed that to staff to see if that was causing problem but still the get_entry method yields none for user.
It is frustrating. I don't really know what I am doing wrong.
Save the user in one var, and then call user.save() because User can't call the method save() try it:
def create_user(request):
if request.method == 'POST':
user_info = forms.UserInfoForm(request.POST)
if user_info.is_valid():
cleaned_info = user_info.cleaned_data
user = User.objects.create_user(username=cleaned_info['username'], password=cleaned_info['password'])
user.save()
render(.......)
Then you need to call auth.authenticate in your function get_entry:
def get_entry(request):
if request.method == 'POST':
user = auth.authenticate(username='testcase', password='test')
if user:
.........
Your code seems to be correct.
The problem might be in the way the params are being passed to your create_user view (Param passing in get_entry view highly unlikely to be a problem since the params username and password are hard-coded).
Try printing out username and password before passing them to User.objects.create_user(), since it's possible that the password field is not being saved properly and/or empty password is being passed, and Django might be creating a hash for the empty password.
P.S.: This is just a speculation, need your response over this for further diagnosis of the issue.
I'm attempting to use Flask and the Flask-Login extension to implement user authentication in a Flask app. The goal is to pull user account information from a database and then log in a user, but I'm getting stuck; however, I've narrowed it down to a particular part of Flask-Login behavior.
According to the Flask-Login documentation, I need to create a user_loader "callback" function. The actual purpose and implementation of this function has had me confused for a few days now:
You will need to provide a user_loader callback. This callback is used
to reload the user object from the user ID stored in the session. It
should take the Unicode ID of a user, and return the corresponding
user object. For example:
#login_manager.user_loader
def load_user(userid):
return User.get(userid)
Now, say I want the user to enter a name and password into a form, check against a database, and log in the user. The database stuff works fine and is no problem for me.
This 'callback' function wants to be passed a user ID #, and return the User object (the contents of which I'm loading from a database). But I don't really get what it's supposed to be checking/doing, since the user IDs are all pulled from the same place anyway. I can 'sort-of' get the callback to work, but it seems messy/hackish and it hits the database with every single resource that the browser requests. I really don't want to check my database in order to download favicon.ico with every page refresh, but flask-login seems like it's forcing this.
If I don't check the database again, then I have no way to return a User object from this function. The User object/class gets created in the flask route for logging in, and is thus out of scope of the callback.
What I can't figure out is how to pass a User object into this callback function, without having to hit the database every single time. Or, otherwise figure out how to go about doing this in a more effective way. I must be missing something fundamental, but I've been staring at it for a few days now, throwing all kinds of functions and methods at it, and nothing is working out.
Here are relevant snippets from my test code. The User class:
class UserClass(UserMixin):
def __init__(self, name, id, active=True):
self.name = name
self.id = id
self.active = active
def is_active(self):
return self.active
The function I made to return the user object to Flask-Login's user_loader callback function:
def check_db(userid):
# query database (again), just so we can pass an object to the callback
db_check = users_collection.find_one({ 'userid' : userid })
UserObject = UserClass(db_check['username'], userid, active=True)
if userObject.id == userid:
return UserObject
else:
return None
The 'callback', which I don't totally understand (must return the User object, which gets created after pulling from database):
#login_manager.user_loader
def load_user(id):
return check_db(id)
The login route:
#app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST" and "username" in request.form:
username = request.form["username"]
# check MongoDB for the existence of the entered username
db_result = users_collection.find_one({ 'username' : username })
result_id = int(db_result['userid'])
# create User object/instance
User = UserClass(db_result['username'], result_id, active=True)
# if username entered matches database, log user in
if username == db_result['username']:
# log user in,
login_user(User)
return url_for("index"))
else:
flash("Invalid username.")
else:
flash(u"Invalid login.")
return render_template("login.html")
My code 'kinda' works, I can log in and out, but as I said, it must hit the database for absolutely everything, because I have to provide a User object to the callback function in a different namespace/scope from where the rest of the login action takes place. I'm pretty sure I'm doing it all wrong, but I can't figure out how.
The example code provided by flask-login does it this way, but this only works because it's pulling the User objects from a global hard-coded dictionary, not as in a real-world scenario like a database, where the DB must be checked and User objects created after the user enters their login credentials. And I can't seem to find any other example code that illustrates using a database with flask-login.
What am missing here?
You will need to load the user object from the DB upon every request. The strongest reason for that requirement is that Flask-Login will check the authentication token every time to ensure its continuing validity. The calculation of this token may require parameters stored on the user object.
For example, suppose a user has two concurrent sessions. In one of them, the user changes their password. In subsequent requests, the user must be logged out of the second session and forced to login anew for your application to be secure. Think of the case where the second session is stolen because your user forgot to log out of a computer - you want a password change to immediately fix the situation. You might also want to give your admins the ability to kick a user out.
For such forced logout to happen, the authentication token stored in a cookie must 1) be based in part on the password or something else that changes each time a new password is set; 2) be checked before running any view, against the latest known attributes of the user object - which are stored in the DB.
I do share your concerns Edmond: hitting database each time when one needs to know user's role or name is insane. Best way would be to store your User object in session or even application-wide cache which gets updated from the DB each couple of minutes. I personally use Redis for that (that way website can be run by multiple threads/processes while using single cache entry point). I just make sure Redis is configured with password and non-default port, and any confidential data (like user hashes etc) are stored there in an encrypted form. Cache can be populated by a separate script running on specified interval, or separate thread can be spawned in Flask. Note: Flask-Session can be also configured to use (the same) redis instance to store session data, in that case instance with 'bytes' datatype will be needed, for a regular cache you might often go with instance type which automatically translates bytes into strings (decode_responses=True).
Here is my code, another User as data mapping object provide query_pwd_md5 method.
User login:
#app.route('/users/login', methods=['POST'])
def login():
# check post.
uname = request.form.get('user_name')
request_pwd = request.form.get('password_md5')
user = User()
user.id = uname
try:
user.check_pwd(request_pwd, BacktestUser.query_pwd_md5(
uname, DBSessionMaker.get_session()
))
if user.is_authenticated:
login_user(user)
LOGGER.info('User login, username: {}'.format(user.id))
return utils.serialize({'userName': uname}, msg='login success.')
LOGGER.info('User login failed, username: {}'.format(user.id))
return abort(401)
except (MultipleResultsFound, TypeError):
return abort(401)
User class:
class User(UserMixin):
"""Flask-login user class.
"""
def __init__(self):
self.id = None
self._is_authenticated = False
self._is_active = True
self._is_anoymous = False
#property
def is_authenticated(self):
return self._is_authenticated
#is_authenticated.setter
def is_authenticated(self, val):
self._is_authenticated = val
#property
def is_active(self):
return self._is_active
#is_active.setter
def is_active(self, val):
self._is_active = val
#property
def is_anoymous(self):
return self._is_anoymous
#is_anoymous.setter
def is_anoymous(self, val):
self._is_anoymous = val
def check_pwd(self, request_pwd, pwd):
"""Check user request pwd and update authenticate status.
Args:
request_pwd: (str)
pwd: (unicode)
"""
if request_pwd:
self.is_authenticated = request_pwd == str(pwd)
else:
self.is_authenticated = False