From bac63e63f8232fb79a044f66d31d38c848c1c64a Mon Sep 17 00:00:00 2001 From: f00wl <f00wl@felinn.org> Date: Sun, 4 Dec 2022 17:25:24 +0100 Subject: [PATCH] rewrite password reset mechanism with JWT * create a JWT instead of reset_token db entry in view * rewrite reset route with JWT based verification --- hiboo/account/login.py | 26 +++++++++++++++++--------- hiboo/user/views.py | 13 +++++++++---- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/hiboo/account/login.py b/hiboo/account/login.py index 52b934d..ad5afca 100644 --- a/hiboo/account/login.py +++ b/hiboo/account/login.py @@ -86,21 +86,29 @@ def signup(): return flask.redirect(utils.url_or_intent(".home")) return flask.render_template("account_signup.html", form=form) - -@blueprint.route("/reset/<token_uuid>", methods=["GET", "POST"]) -def reset(token_uuid): - token = models.ResetToken.query.get(token_uuid) or flask.abort(403) - if token.updated_at is not None or token.expired_at < datetime.datetime.now(): +@blueprint.route("/reset", methods=["GET", "POST"]) +def reset(): + token = flask.request.args.get('token') or flask.abort(403) + key = flask.current_app.config["SECRET_KEY"] + jwt = JsonWebToken(['HS512']) + claims_options = { + 'exp': {'essential': True, 'value': datetime.datetime.now().timestamp()}, + 'aud': {'essential': True, 'value': flask.url_for('.reset')}, + 'user_uuid': {'essential': True} + } + try: + claims = jwt.decode(token, key, claims_options=claims_options) + claims.validate() + user = models.User.query.get(claims["user_uuid"]) or flask.abort(404) + except Exception as e: flask.flash(_("Invalid or expired reset link"), "danger") return flask.redirect(flask.url_for(".signin")) form = forms.PasswordForm() del form.old if form.validate_on_submit(): - token.expired_at = datetime.datetime.now() - models.db.session.add(token) - auth = token.user.auths[models.Auth.PASSWORD] + auth = user.auths[models.Auth.PASSWORD] auth.set_password(form.password.data) - models.log(models.History.PASSWORD, user=token.user) + models.log(models.History.PASSWORD, user=user) models.db.session.add(auth) models.db.session.commit() flask.flash(_("Successfully reset your password"), "success") diff --git a/hiboo/user/views.py b/hiboo/user/views.py index 5ea5874..86b4cdd 100644 --- a/hiboo/user/views.py +++ b/hiboo/user/views.py @@ -35,10 +35,15 @@ def details(user_uuid): def password_reset(user_uuid): user = models.User.query.get(user_uuid) or flask.abort(404) expired = datetime.datetime.now() + datetime.timedelta(days=1) - token = models.ResetToken(user=user, expired_at=expired) - models.db.session.add(token) - models.db.session.commit() - reset_link = flask.url_for("account.reset", token_uuid=token.uuid, _external=True) + payload = { + "exp": int(expired.timestamp()), + "aud": flask.url_for('account.reset'), + "user_uuid": user.uuid + } + header = {"alg": "HS512"} + key = flask.current_app.config["SECRET_KEY"] + token = jwt.encode(header, payload, key) + reset_link = flask.url_for("account.reset", token=token, _external=True) flask.flash(_("Reset link: {}").format(reset_link), "success") return flask.redirect(flask.url_for(".details", user_uuid=user.uuid)) -- GitLab