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.
Related
I am working on a basic login form for a hybrid React/Django web app. I would like to use the built in data-cleaning and validating methods of the Django Form models, but our frontend is pure React. Everything works as far as logging in, but I am feeding the raw body data into the authenticate function as shown here.
def login_view(request):
if request.method == "POST":
form_data = json.loads(request.body.decode('utf-8'))
user = authenticate(request, email=form_data["username"], password=form_data["password"])
if user == None:
request.session["invalid_user"] = 1
logging.warning("Login form contains no user")
login(request, user)
My question is, is there any way to feed this form_data into the Django native LoginForm when I instantiate it? I would prefer to not recode all of the input validation that Django does already.
I've tried instantiating a LoginForm like so:
form = LoginForm(data=form_data)
And then tried running form.full_clean(), but it doesn't seem to work. Any help would be greatly appreciated, thank you!
The issue was actually a difference in the variable names between the rendered React form and the Django LoginForm I had defined. One was username and password, the other email and password. With that in mind the usual way of working with forms works great!
if request.method == "POST":
form_data = json.loads(request.body.decode('utf-8'))
form = LoginForm(data=form_data)
if form.is_valid():
email = form.cleaned_data["email"]
password = form.cleaned_data["password"]
user = authenticate(request, email=email, password=password)
if user is not None:
login(request, user)
I want to use django's default password reset view "PasswordResetView" which let's the user reset his password when he forgets it in a template that already has a view that i built on my own, after looking at the tutorials and the questions i found how to use it only on a different template that is made only for the password reset, but i don't want the user to go to a different page just to change his password when he forgets it, i want to make it in a bootstrap modal in the home page.
here is my home view that i want to add PasswordResetView functionality to it:
def home(request):
user = request.user
signin_form = SigninForm()
signup_form = SignupForm()
if request.method == "POST":
if 'signin_form' in request.POST:
signin_form = SigninForm(request.POST)
if signin_form.is_valid():
email = request.POST['email']
password = request.POST['password']
user = authenticate(email=email, password=password)
if user:
login(request, user)
elif user is None:
messages.error(request, 'ُEmail or password is incorrect')
if 'signup_form' in request.POST:
signup_form = SignupForm(request.POST)
if signup_form.is_valid():
signup_form.save()
full_name = signup_form.cleaned_data.get('full_name')
email = signup_form.cleaned_data.get('email')
raw_password = signup_form.cleaned_data.get('password1')
account = authenticate(email=email, password=raw_password)
login(request, account)
context = {'signin_form': signin_form,'signup_form': signup_form}
return render(request, 'main/home.html', context)
PS: i tried copy pasting the source code of that view (PasswordResetView) from django's source code in my view but i found some errors because it's a class based view, so if you find this the proper way, guide me to do it
or if i can't merge them somehow how to create a custom one
this is what i found in the other answers which lets you use it in a certain template that has only that view (PasswordResetView) which is not what i want:
from django.contrib.auth import views as auth_views
path('password_reset/', auth_views.PasswordResetView.as_view(template_name="myapp/mytemplate.html",form_class=mypasswordresetform),name="reset_password"),
I'll give you a simple approach to having a password reset feature on your django application. Before having any code, let me give a brief exlanation of the process. What you want to do is get a user to input their email, check if there is any user with that email, then if there is one, send an email to that address with a uniquely generated link.
From this link, you should be able to extract the user object which you need to change password. An example would be to use django's signing module. This link will simply need to redirect the user to a template where there is a form with 2 fields i.e. New Password and Verify Password.
Django's generic views come with this functionality out-of-the-box if you are using Django's authentication module, but you aren't forced to use it, but its best to do so.
Here I'll only show you how to collect the email address on the same view as you said you wanted.
def home(request):
# ...your other code
if request.method == 'post':
if 'reset_password' in request.POST:
email = request.POST.get("email", "")
user_qs = User.objects.filter(email=email)
if not user_qs.exists():
# send error message to user here
else:
user = user_qs.get()
# send email with uniquely generated url here.
The other aspects of generating a URL and sending the mail, I believe you can research these separately. But I hope you now have an idea of where and what to search.
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
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.
I am using Flask-Login and trying to understand how it works. If I understand the docs right, I should create a user class which holds four methods, is_authenticated, is_active, is_anonymous and get_id. I don't really understand what I should do after that. There is a function, login_user(user), which I understand would take my instance of a not yet authenticated user and authenticate it, but how does that work when my user class does not have any set methods?
Here is some code:
class User:
isAuth = False
isActive = False
isAnon = True
id = unicode(0)
def __init__(self, isAuth, isActive, isAnon, id):
self.isAuth = isAuth
self.isActive = isActive
self.isAnon = isAnon
self.id = id
def is_authenticated(self):
return isAuth
def is_active(self):
return isActive
def is_anonymous(self):
return isAnon
def get_id(self):
return id
And here is my guess about how I should authenticate a user, by using authomatic and Flask-Login:
#app.route('/login/<provider_name>/', methods=['GET', 'POST'])
def login(provider_name):
response = make_response()
result = authomatic.login(WerkzeugAdapter(request, response), provider_name)
user = User()
if result:
if result.user:
result.user.update()
login_user(user)
return render_template('login.html', result=result)
return response
What am I missing?
EDIT, here is the user_loader:
#lm.user_loader
def load_user(userid):
return User.get(userid)
And I also have this on top:
lm = LoginManager()
lm.init_app(app)
When defininig the User model, you have to have those four methods implementend (It's how Flask-Login works). However, you don't have to define them yourself, unless you need some custom behaviour. Flask-Login provides you with the UserMixin class. All you have to do is subclass your user model and UserMixin will provide all four methods with default behaviour for you. For example:
from flask.ext.login import UserMixin
class User(UserMixin, Model):
# your user model definition
Inside your User model you can override any of these four methods to suit your needs.
Also, please refer to the bottom of "Your User Class" section for further reference:
https://flask-login.readthedocs.org/en/latest/#your-user-class
For fetching user data you should use a form. The way you put it, user would have to enter his/her directly data into the URL (I'm guessing something like /login?username=yourusername&password=youruserpassword) which isn't secure and is generally considered a bad practice.
Instead, you should use forms for your users to enter their login data in. WTForms and Flask-WTF extensions provide you with the way to easily create forms. For example:
from flask_wtf import Form
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import Required
class LoginForm(Form):
username = StringField('Your username', validators=[Required()])
password = PasswordField('Your password', validators=[Required()])
submit = SubmitField('Sign In')
For further reference:
https://wtforms.readthedocs.org/en/latest/ and
https://flask-wtf.readthedocs.org/en/latest/
After that your view should just capture the form data, check if the user with provided data exists in the database and if the provided password matches the one stored in database. This is how your view would like:
#app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
# on POST check if the submitted form is valid
if form.validate_on_submit():
# get user from database
user = User.query.filter_by(username=form.username.data).first()
#check if the user exists and if he/she provided correct password
if user is not None and user.password == form.password.data:
# if everything checks out, call the login function
login(user)
# redirecting after form submission is also considered good practice
redirect(url_for('somewhere'))
# if user is not found or he/she provided wrong password, inform them
flash('Invalid username and/or password')
# on GET, just render the template with login form
return render_template('login.html', form=form)
As a side note, this view assumes you are storing passwords in plain-text, which is a bad idea as someone pointed out. I made it this way just to demonstrate login functionality.
Also, if you wonder how to define models and how to query data from database, refer to Flask-SQLAlchemy https://pythonhosted.org/Flask-SQLAlchemy/ and SQLAlchemy http://www.sqlalchemy.org/
I found this to be very helpful:
https://www.openshift.com/blogs/use-flask-login-to-add-user-authentication-to-your-python-application
Please remember never to store plain-text passwords in a DB for user authentication.
Are you still stuck? I'm here if you need me :)