8

I am new to flask, recently learned about flask_security/flask_login/flask_user.

I wish that somehow I could use flask_login along with flask-JWT, for the REST API.
Basically, I'd like to have the features like remember-me, forgot-password etc, from the flask_login
Upon searching, I found that it couldn't be done on the same flask view.

Could somebody guide me, how to do it?
Thanks.

piet.t
  • 11,718
  • 21
  • 43
  • 52
penka_the_cow
  • 81
  • 1
  • 4

2 Answers2

10

flask-login provides the request_loader callback exactly for this purpose, for authenticating requests in a custom way.

In my case, I added this to my create_app function:

@login_manager.request_loader
def load_user_from_request(request):
    auth_headers = request.headers.get('Authorization', '').split()
    if len(auth_headers) != 2:
        return None
    try:
        token = auth_headers[1]
        data = jwt.decode(token, current_app.config['SECRET_KEY'])
        user = User.by_email(data['sub'])
        if user:
            return user
    except jwt.ExpiredSignatureError:
        return None
    except (jwt.InvalidTokenError, Exception) as e:
        return None
    return None

Otherwise, I followed this tutorial, so the token is created like this (in the login function):

token = jwt.encode({
'sub': user.email,
'iat':datetime.utcnow(),
'exp': datetime.utcnow() + timedelta(minutes=30)},
current_app.config['SECRET_KEY'])

This way you can just use @login_required from flask-login instead of defining a custom decorator to protect views.

I used PyJWT instead of Flask-JWT since it seems Flask-JWT is discontinued.

Kris
  • 1,358
  • 13
  • 24
  • 3
    To piggy back on this, if you use flask-jwt-extended, which is still actively maintained unlike flask-jwt, you can use the built in verifier methods so you don’t have to duplicate the JWT logic and still get the benefits of the rest of the extension: https://flask-jwt-extended.readthedocs.io/en/latest/api.html#verify-tokens-in-request – vimalloc Jan 03 '19 at 15:05
  • 1
    But can you use it together with flask-login? Looks like flask-jwt-extended provides its own decorators for protecting view functions. Depends on your use case of course, but for me the main point was that I could introduce jwt auth to an app that already heavily relies on flask-login. – Kris Jan 04 '19 at 06:35
  • 1
    Those links I provided are not decorators. They are just normal functions that you could utilize inside of the request_loader decorated function. I haven’t looked into into it too closely but I imagine they could work together. Now if you really want to mix and match two similar extensions like that might be another story. – vimalloc Jan 04 '19 at 13:15
  • 1
    I see. You would call into flask-jwt-extended from the request_loader callback to verify the token. – Kris Jan 05 '19 at 05:18
-1

there are two ways to reach your question solution:

  1. jwt :

first import your dependencies :

import jwt
from functools import wraps
from flask import session, request, abort, jsonify, 
   render_template, make_response, Blueprint, current_app
from flask_session import Session
import datetime

now you need a decorator called token_required() :

def token_required(Admin_Permission=False):
def wrapper(func):
    @wraps(func)
    def inner(*args, **kwargs):
        token = session.get("token")
        if not token:
            return jsonify({"message": "Token Is Missing"})
        try:
            data = jwt.decode(token, current_app.secret_key,
                              algorithms=["HS256"])
            current_user = UserModel.find_by_id(data["user_id"])

            if current_user is None:
                return jsonify({"message": "Token Is Invalid"})

            # for having some features Ignore this
            if not (verify_admin(current_user=current_user, admin_Permission=Admin_Permission)):
                return jsonify({"message": "ADMIN"})

        except Exception as e:
            return jsonify({"Error": e})
        return func(current_user, *args, **kwargs)
    return inner
return wrapper

your init.py file or your app.py file should be like this or at least set these configs :

from flask import Flask
from flask_session import Session
import uuid
from src.db import db
from src.Users.routes import blp as UserBlueprint

def create_app():

app = Flask(__name__)


app.config["SECRET_KEY"] = str(uuid.uuid4())
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///user.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config["SESSION_PERMANENT"] = False
app.secret_key = 'super secret key'
app.config['SESSION_TYPE'] = 'filesystem'
se = Session(app)
db.init_app(app)
app.register_blueprint(UserBlueprint)
return app

third your login route :

   @blp.route('/login', methods=["GET", "POST"])
   def Login():
       if request.method == "POST":
          auth = request.form
          if auth:
             user = UserModel.find_by_username(auth.get("username"))
             if user and user.password == auth.get("password"):
                token = jwt.encode({"user_id": user.id,
                "exp": datetime.datetime.utcnow() + 
                datetime.timedelta(seconds=30)},
                current_app.secret_key, algorithm="HS256")
                session["token"] = token
                return jsonify({"Current_Token": token})
          return render_template("UserTemplates/signin.html")

   @blp.route('/protected', methods=["GET", "POST"])
   @token_required(Admin_Permission=True)
   def protected(current_user):
       print(current_user)
       return f"This Link Is protected and you have the secret key. and this is current user {current_user.username} {current_user.email}"
Erfan
  • 1
  • 1