Once I get to the verify_token function it keeps executing the except statement instead of returning the value in 'id_user' and I'm not sure why. I am using these libraries. flask-login, sqlalchemy, itsdangerous for jsonwebserializer, and wtforms.
Functions
def get_reset_token(user):
serial = Serializer(app.config['SECRET_KEY'], expires_in=900) # 15 mins in seconds
return serial.dumps({'id_user':user.id}).decode('utf-8')
def verify_token(token):
serial = Serializer(app.config['SECRET_KEY'])
try:
user_id = serial.load(token)['id_user']
except:
return None
return Users.query.get('id_user')
def send_mail(user):
token = get_reset_token(user)
message = Message('Password Reset Request', recipients = [user.email], sender='noreply#gmail.com')
message.body= f'''
To Reset your password, click the following link:
{url_for('reset_token', token = token, _external = True)}
If you did not send this email, please ignore this message.
'''
mail.send(message)
ROUTES
#app.route('/password_reset', methods = ['GET', 'POST'])
def password_reset():
form = Password_request()
if request.method == "POST":
if form.validate_on_submit:
user = Users.query.filter_by(email = form.email.data).first()
send_mail(user)
flash('Check your email. Password change request has been sent')
return redirect(url_for('login'))
else:
flash('Your email was not linked to an account')
return render_template('password_reset.html', form = form)
#app.route('/password_reset/<token>', methods = ['GET', 'POST'])
def reset_token(token):
user = verify_token(token)
if user == None:
flash('The token is invalid or expired')
return redirect(url_for('password_reset'))
form = Password_success()
if form.validate_on_submit:
hashed_password=generate_password_hash(form.password.data, method = 'sha256')
user.password = hashed_password
db.session.commit()
flash('Your password has been updated!')
return redirect(url_for('signup'))
def verify_token(token):
serial = Serializer(app.config['SECRET_KEY'])
try:
user_id = serial.load(token)['id_user']
except:
return None
return Users.query.get('id_user') # this looks wrong
Shouldn't the last line of verify_token be return Users.query.get(user_id)? You're assigning the value of the token to that variable , then ignoring it and telling SQLAlchemy to find a record with the ID of the string value 'id_user' which I doubt is what you're intending to do.
def verify_token(token):
serial = Serializer(app.config['SECRET_KEY'])
try:
user_id = serial.load(token)['id_user']
except:
return None
return Users.query.get(user_id) # What happens when you change this?
Related
Every time a new user is connected to the server I got a list of the users that refresh to show them in my page. I'm using a boolean for that, every time a user is connecting their boolean changes to True and my list is looking for users with the boolean True only. I'm trying now to change the boolean back to False when they sign out. How can I do this?
#app.route("/sign-in", methods=["GET", "POST"])
def signin():
form = forms.SignInForm()
if flask.request.method == "POST":
if form.validate_on_submit():
username = form.username.data
password = form.password.data
# Retrieve the user that matches this username
user = models.User.query.filter_by(name=username).first()
print(user.boolean)
# Check the provided password against the user's one
if user is not None and user.password == password:
flask_login.login_user(user)
flask.flash(f"{user.name} logged in successfully !", "success")
idn = user.id
user.boolean = True
db.session.commit()
print(user.boolean)
return flask.render_template('user_page.html', user=user)
else:
flask.flash("Something went wrong.", "danger") # Put the message into the flashed messages
# To retrieve those messages: flask.get_flashed_messages()
return flask.render_template("signin.html", form=form)
#app.route("/sign-out", methods=["GET"])
def signout():
flask_login.logout_user()
return flask.redirect('/sign-in')
Put it in the signout route:
#app.route("/sign-out", methods=["GET"])
def signout():
user = models.User.query.filter_by(id=current_user.id).first()
user.boolean = False
db.session.commit()
flask_login.logout_user()
return flask.redirect('/sign-in')
I'm trying to have a FORGOT PASSWORD? functionality in my website, I'm simply generating a token with a user id and then I send the form where they can update their password along with this token, but the problem is that I can't actually get that token after I click on the link, I get none, can someone tell me what I'm missing exactly? here's what I've done so far:
# generates the token with the user id
def get_reset_password_token(user_id, expires_in=1800):
return jwt.encode({'reset_password': user_id, 'exp': time() + expires_in},
app.config['SECRET_KEY'], algorithm='HS256').decode('utf-8')
# verfies the token recovered from the link
def verify_reset_password_token(token):
try:
id = jwt.decode(token, app.config['SECRET_KEY'],algorithms=['HS256'])['reset_password']
except:
print("token was invalid.")
return None
return print("all good")
# sends email to the user containing reset password link
def send_password_reset_email(user_id, user_mail):
token = get_reset_password_token(user_id)
msg = Message('[El-menu] Password Reset Request', sender ='noreply#demo.com', recipients =[user_mail])
msg.body = f'''To reset your password, visit the following link:
{url_for('reset_password', token=token , _external=True)}
If you didn't make this request, ignore this email and no changes will be madeSincerely,
Sincerely,
El-menu Team
'''
mail.send(msg)
return print("email sent with token " + token +" to email " + user_mail, file=sys.stderr)
#app.route("/reset_password_request", methods = ["GET", "POST"])
def reset_password_request():
if request.method == "POST":
email = request.form.get("email")
# checks for valid mail
if not email:
error = "you must submit a valid email"
return render_template("reset_password_request.html", error = error)
# performs a query to find information about given email, whether it's in the system or not?
user = db.execute("SELECT * FROM users WHERE email= :email", email = email)
if user:
user_mail = user[0]['email']
user_id = user[0]['id']
send_password_reset_email(user_id, user_mail)
password_reset = "Password reset link has been sent to your email!"
return render_template("login.html", password_reset = password_reset)
else:
return render_template("reset_password_request.html")
#app.route('/reset_password/<token>', methods = ["GET", "POST"])
def reset_password(token):
if request.method == "POST":
print("trying to reset password now..", file=sys.stderr)
print(token, file=sys.stderr)
user_id = verify_reset_password_token(token)
print(user_id, file=sys.stderr)
if not user_id:
print("token is none", file=sys.stderr)
return redirect(url_for('index'))
print(user_id, file=sys.stderr)
password = request.form.get("password")
confirmation = request.form.get("confirmation")
# Ensure password was submitted
if not password:
error = "You must provide a password"
return render_template("reset_password.html", error = error)
# Ensure confirmation was submitted
elif not confirmation:
error = "You must provide a password confirmation"
return render_template("reset_password.html", error = error)
reg = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d#$!#%*?&]{8,}$"
# compiling regex
pat = re.compile(reg)
# searching regex
mat = re.search(pat, password)
# validating conditions
if not mat:
error = "Password is weak"
return render_template("reset_password.html", error = error)
# Ensure Passwords match
if password != confirmation:
error = "Password and confirmation must match!"
return render_template("reset_password.html", error = error)
# Hash password
hashed_password = generate_password_hash(password)
db.execute("UPDATE users SET hash = :hash WHERE id = :id", hash = hashed_password, id = user_id)
return redirect("/")
else:
return render_template("reset_password.html")
I guess that's because the link you give {url_for('reset_password', token=token , _external=True)} in send_password_request_email makes a GET request so the user gets in reset_password and directly gets returned return render_template("reset_password.html")
You need to use render_template in order to render an email body that understands what token is.
So the first step would be to create an email template for the body of the email. The next step would be to use render_template and pass the token in.
Example from one of my Flask apps:
message = Message(
subject=render_template("email/reset_subject.txt"),
recipients=[user.email]
)
salt = current_app.config.get("SECURITY_PASSWORD_SALT", b"")
message.html = render_template(
"email/reset.html",
token=generate_code(user.email, salt=salt)
)
mail.send(message)
After authenticating a user, I am having trouble keeping a user logged in as they move through the website I have built. I have tried using cookies and sessions but I can't seem to get it to work.
I am sharing my code for login where I grab data to authenticate a user from an excel sheet that is populated at Sign Up
I locate the names of each team member to have them appear on the Team Page, which is where the user is redirected to after login
#app.route('/login',methods=['POST','GET'])
def login():
# get data from form
try:
username = str(request.form['username2'])
password2 = str(request.form['password2'])
teamname = str(request.form['teamname'])
print(username)
print(password2)
# read excel sheet
data = pd.read_excel("C:\\Users\\290002925\\Documents\\registration.xlsx")
for name in data['Team']:
if name == teamname:
team_info = data.loc[data['Team'] == teamname]
print(team_info)
team_info['FullName'] = team_info['FirstName'] + [' '] + team_info['LastName']
teamroster = team_info[['FullName']].copy()
finalteamroster = pd.DataFrame(teamroster, index=range(5))
print(finalteamroster)
print(len(finalteamroster))
finalteamroster.fillna(value='', inplace=True)
print(finalteamroster)
amember1 = finalteamroster.at[0,'FullName']
print(amember1)
amember2 = finalteamroster.at[1,'FullName']
print(amember2)
amember3 = finalteamroster.at[2,'FullName']
print(amember3)
amember4 = finalteamroster.at[3,'FullName']
print(amember4)
amember5 = finalteamroster.at[4,'FullName']
print(amember5)
This is where the authentication of the user comes in
for user in data['Email']:
#check username
if user == username:
passw = data.loc[data['Email'] == username]
# check password
print(passw)
test2 = passw.iat[0,3]
This is how I wrote out cookies but I think Session might be more useful
if test2 == password2:
# authenticated
print("User is authenticated")
response = render_template('teampage.html', teamname=teamname, member1=amember1, member2=amember2, member3=amember3, member4=amember4, member5=amember5)
response.set_cookie('userID', username)
return response
else:
print("Password is incorrect")
return render_template('login.html')
else:
print("Username is incorrect")
return render_template('index.html')
except:
print("fail")
return render_template('login.html')
Code for Teampage
#app.route('/teampage')
def teampage():
userid = request.cookies.get('userID')
return render_template('teampage.html', userid=userid)
HTML user reference
{{userid}}
It is a bit hard to understand your initial code but what I understand is that you need to maintain a User session-
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html', title=': Login')
if request.method == 'POST':
if YourMethodToCheckPassword==True:
session['username'] = request.form['user']
return redirect(url_for('home'))
And then use the Session object everywhere like-
#app.route('/home')
def home():
if 'username' in session:
return render_template("index.html", name=session['username'])
Inside your HTML use
{{ name }}
I have a modular app with blueprint structure. I registered the blueprints and they are working fine. I am having problems with the url_for with parameters:
werkzeug.routing.BuildError: Could not build url for endpoint 'confirm_email' with values ['token']
here is my the endpoint in home.py
#home.route('/register',methods=['GET','POST'])
def register():
form = RegistrationForm(request.form)
print(form, file=sys.stderr)
print(form.email, file=sys.stderr)
if request.method == 'POST' and form.validate():
u = User(
email = form.email.data,
password = form.password.data
)
email_ddbb = User.query.filter_by(email=form.email.data).first()
# we register the user
if email_ddbb is None:
# Now we'll send the email confirmation link
subject = "Confirm your email"
token = ts.dumps(u.email, salt='email-confirm-key')
print(url_for('user.activate',token=token,_external=True))
confirm_url = url_for('user.activate',token=token,_external=True)
html = render_template('mail/activate.html', confirm_url=confirm_url)
# we send the email from utils.py
send_email(u.email, subject, html)
db.session.add(u)
db.session.commit()
flash('Thanks for registering, please check your inbox')
return redirect(url_for('home.index'))
else:
flash('Email already in use')
return redirect(url_for('home.index'))
#TODO MUST BE UNIQUE
else:
flash('Please enter a valid email address')
return redirect(url_for('home.index'))
and here is the other endpoint in user.py
#user.route('/confirm/<token>')
def activate(token):
try:
email = ts.loads(token, salt="email-confirm-key", max_age=86400)
except:
abort(404)
user = User.query.filter_by(email=email).first_or_404()
user.verified = True
db.session.add(user)
db.session.commit()
return redirect(url_for('home.index'))
Okey, the problem was not in there but in the mailing:
def send_email(to, subject, template, **kwargs):
msg = Message(app.config['MAIL_SUBJECT_PREFIX'] + subject,
sender=app.config['MAIL_SENDER'], recipients=[to])
msg.body = ''
token = ts.dumps(to, salt='email-confirm-key')
confirm_url = url_for(
'confirm_email', <-- should be user.activate
token=token,
_external=True)
msg.html = render_template(
'activate.html',
confirm_url=confirm_url)
mail.send(msg)
I'm new to python/flask and am trying to build app using flask-login. I can't get my head around the error I'm getting "object has no attribute 'get_id'" Appreciate your help.
class User(UserMixin):
def __init__(self, email, id, active=True):
self.email = email
self.id = id
#self.active = active
def get_id(self):
return self.id
def is_active(self):
# Here you should write whatever the code is
# that checks the database if your user is active
return True
def is_anonymous(self):
return False
def is_authenticated(self):
return True
login_manager = LoginManager()
login_manager.init_app(app)
# load_user .. never makes it till here
#login_manager.user_loader
def load_user(userid):
try:
print 'this gets executed:--', userid
return User.get(User.id==userid)
except User.DoesNotExist:
return None
#app.route('/confirm/<token>', methods=['GET', 'POST'])
def confirm_email(token):
try:
email = confirm_token(token)
print email
except:
flash('The confirmation link is invalid or has expired.', 'danger')
userExists = db.userExists(email)
if userExists:
flash('Account already confirmed. Please login.', 'success')
login_user(userExists, force=True, remember=True)
else:
flash('You have confirmed your account. Thanks!', 'success')
confirm_login()
login_user(userExists, force=True, remember=True)
return redirect(url_for('Hello'))
code for db
def userExists(email):
SQL = """SELECT * FROM all_users WHERE email = %s"""
data = (email,)
records = runQuery(SQL, data)
if records and records[0]:
return records[0][0]
else:
records
Error that I'm getting:
File "/Documents/Dev/Ottawa-Final/ottawa-final/app/network_of_innovators.py", line 310, in confirm_email
login_user(userExists, force=True, remember=True)
File "/Documents/Dev/lib/python2.7/site-packages/flask_login.py", line 678, in login_user
user_id = getattr(user, current_app.login_manager.id_attribute)()
AttributeError: 'str' object has no attribute 'get_id'
In confirm_email, userExists is a string (I'm guessing the users email returned by your userExists() method). login_user() expects its first parameter to be an object but you're passing a string, hence your error.
The solution is to create a User object first and then pass that to login_user()
Ex. You should be doing something like:
userExists = db.userExists(email)
if userExists:
flash('Account already confirmed. Please login.', 'success')
user_id = db.getUserId(email) # Get the User's id somehow
user = User(email, user_id)
login_user(user, force=True, remember=True)