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)
Related
The JSON response of my endpoint returns {} even though I am logging the correct data.
from flask_smorest import Blueprint
bp = Blueprint("auth", __name__, url_prefix="/api/v1/auth/")
#bp.route("/login", methods=["POST"])
#bp.arguments(LoginRequest)
#bp.response(200, JwtTokenResponse)
#bp.response(404, ErrorResponse)
def login(args):
current_app.logger.debug(args)
username = args.get("username", None)
password = args.get("password", None)
current_app.logger.debug(f"Username: {username}")
current_app.logger.debug(f"Password: {password}")
user = User.query.filter_by(username=username).first()
if user is None:
return dict(message="User does not exists"), 404
if not check_password_hash(user.password, password):
return dict(message="Unable to Authenticate user."), 404
access_token = create_access_token(identity=username)
refresh_token = create_refresh_token(identity=username)
response = dict(access_token=access_token, refresh_token=refresh_token)
current_app.logger.debug(f"Response: {response}")
return response, 200
My LoginTokenSchema and ErrorResponse schemas are defined as:
from marshmallow import Schema, fields
class JwtTokenResponse(Schema):
access_token = fields.String()
refresh_token = fields.String()
class ErrorResponse(Schema):
message = fields.String()
When I test the API with a user not in the database or with the wrong password; it will product the correct response with ErrorRespose however with the correct creds it just will output {}, when I check the flask logs I can see access/refresh token dict, what am I doing wrong?
You don't need to call Blueprint.response twice, which is what I did in my question. You can call alt_response with a custom error message or just use abort and let flask-smorest take care of the rest.
#bp.route("/login", methods=["POST"])
#bp.arguments(LoginRequest)
#bp.response(200, SmorestJWTTokenResponse)
def login(args):
current_app.logger.debug(args)
username = args.get("username", None)
password = args.get("password", None)
user = User.query.filter_by(username=username).first()
if user is None:
abort(404, message="User does not exists.")
if not check_password_hash(user.password, password):
abort(404, message="Unable to Authenticate user.")
access_token = create_access_token(identity=username)
refresh_token = create_refresh_token(identity=username)
return dict(access_token=access_token, refresh_token=refresh_token), 200
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?
kindly help it out folks! I am new to programming. I am trying to Authenticate Login. But its showing "TypeError....." i couldn't figure out.
See Below This is My code (main.py)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
email = request.form.get('EMAIL')
con1 = sqlite3.connect("check10.db")
cur = con1.cursor()
data = cur.execute("SELECT EMAIL FROM register WHERE EMAIL = ? ", (email,))
if data == email:
return render_template('homepage.html')
else:
alert = "Check Credentials "
return render_template('loginpage.html', alert)
return render_template("loginpage.html")
Try this:
from flask import request, render_template, flash
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
email = request.values.get('EMAIL')
con1 = sqlite3.connect("check10.db")
cur = con1.cursor()
data = cur.execute("SELECT EMAIL FROM register WHERE EMAIL = ? ", (email,))
if data == email:
return render_template('homepage.html')
else:
flash('Check Credentials ')
return render_template('loginpage.html')
return render_template("loginpage.html")
Try also to change the variable email like:
email = request.args.get('EMAIL')
You are getting error because of return render_template('loginpage.html', alert).
If you want to send some data from server to HTML page then you can refer to link.
If you want flash message 'check credentials' to the user then refer to this Link
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)
I'm using Flask-JWT-Extended to protect my Flask API. After the Login, in the protected route (add-user), I call get_jwt_identity(), but it's returning None, so I'm not able to get the identity.
#flask_app.route('/<api_version>/login', methods=['POST'])
def login(api_version):
print(f'login', request.form)
response = None
try:
username = request.form['username']
password = request.form['password']
if not username:
return jsonify({"msg": "Missing username parameter"}), 400
if not password:
return jsonify({"msg": "Missing password parameter"}), 400
user = User.get_with_password(username, password)
if (not user):
e1 = Unauthorized(
'Invalid username or password. Please try again.')
e1.status = 401
raise e1
""" flask_login.login_user(user, remember=True) """
access_token = create_access_token(identity=username)
response = json.dumps({"token": access_token}, cls=CustomJSONEncoder)
except Exception as e:
errMsg = f'Error Logging in user {username if username else ""}: {e}'
status = e.status if hasattr(e, 'status') else 500
print(f'{errMsg}')
traceback.print_exc()
return Response(
json.dumps({"message": errMsg, "status": status, "stack": traceback.format_exc() }), status=status, mimetype='application/json')
resp = Response(response, status=200, mimetype='application/json')
return resp
#flask_app.route('/<api_version>/add-user', methods=['POST'])
#jwt_required
def add_user(api_version):
print(f'add-user', request)
response = None
username = None
password = None
allow_admin = None
try:
data = request.get_json()
print(f'add-user data', data)
if 'username' in data:
username = data['username']
else:
return jsonify({"msg": "Missing username parameter"}), 400
if 'password' in data:
password = data['password']
else:
return jsonify({"msg": "Missing password parameter"}), 400
if 'allow_admin' in data:
allow_admin = data['allow_admin']
""" user = User.get_with_password(username, password)"""
user = get_jwt_identity()
print('user',user)
if (not user):
e1 = Unauthorized(
'Invalid username or password. Please try again.')
e1.status = 401
raise e1
response = json.dumps({"user": user}, cls=CustomJSONEncoder)
except Exception as e:
errMsg = f'Error Adding User {username}: {e}'
status = e.status if hasattr(e, 'status') else 500
print(f'{errMsg}')
traceback.print_exc()
return Response(
json.dumps({"message": errMsg, "status": status, "stack": traceback.format_exc() }), status=status, mimetype='application/json')
resp = Response(response, status=200, mimetype='application/json')
return resp
User.py
class User():
#classmethod
def get_with_password(cls, username, password):
print(f'User get_with_password {username} with password')
user_db = account.get_account(username)
print(f'User returned from DB: {user_db}')
user = User()
if not user_db or not len(user_db) or (not 'password' in user_db):
return None
user.username = username
user.id = username
if bcrypt.check_password_hash(user_db['password'], password):
user.role = user_db['role']
#user.is_authenticated = True
print(
f'loginUser returning {vars(user)} ')
return user
return None
I also faced same issue, in my case i forgot to add #jwt_required
#jwt_required
def get(self):
uid = get_jwt_identity()
print("self.uid",uid)
in your code #jwt_required is there, check may be token has expired
I've got the same issue and got it resolved by the following steps
Step1: Check if you have wrapped your app (flask_app) like the following.
from flask_jwt_extended import JWTManager
jwt = JWTManager(flask_app)
Step2: Check if you have JWT_BLACKLIST_ENABLED in your app or not.
If JWT_BLACKLIST_ENABLED = False or JWT_BLACKLIST_ENABLED not present in your app, add the following method above where ever you have used create_token_method() (in your case it is login method).
#jwt.user_claims_loader
def add_claims_to_access_token(identity):
return {
'identity': identity
}
If JWT_BLACKLIST_ENABLED = True then add the following also above your login method.
#jwt.token_in_blacklist_loader
def check_if_token_in_blacklist(decrypted_token):
jti = decrypted_token['jti']
return jti in blacklist