Passing the destination URL through Flask and Flask-Login

Viewed 194

Looking at the Flask-Login docs and several relatively old threads on Stackoverflow, I am concerned about the security of my solution for passing the restricted page URL through the login process.

First, I was getting Attribution Error when trying to use @login_manager.unauthorized_handler. ("login_manager does not have an attribute unauthorized_handler.") This a separate issue altogether, because it really should have worked. (See app factory below.)

When I applied a redirect_destination function without the modified @login_manager.unauthorized_handler, the login failed to redirect to the target destination.

def redirect_destination(dest_url, fallback):
    try:
        dest_url = url_for(dest_url)
    except:
        return redirect(fallback)
    return redirect(dest_url)

Then I applied a session instance to be passed from the destination URL through login along with the fresh_login_required decorator and .is_authenticated property instead of the standard login_required.

target blueprint:

@target.route('/target', methods=['GET', 'POST'])
@fresh_login_required
def target():
    if current_user.is_authenticated:
        ...
        return render_template('target.html')
    else:
        session['dest_url']=request.endpoint
        return redirect(url_for('account.login'))

auth blueprint:

@account.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('main.index'))
    dest_url = session.get('dest_url')
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()

        if not user:
            return redirect(url_for('account.login'))

        if user.check_password(form.password.data):
            login_user(user, remember=form.remember_me.data)
            ...
            return redirect_destination(dest_url, fallback=url_for('main.index'))
        else:
            return redirect(url_for('account.login'))

    return render_template('account/login.html')

app factory:

...

login_manager = LoginManager()
login_manager.login_view = 'account.login' #hard-coded view
login_manager.refresh_view = 'account.refresh' #hard-coded view

def create_app():  
    app = Flask(__name__,
                static_url_path='/',
                static_folder='../app/static',
                template_folder='../app/templates')
            
    db.init_app(app)
    login_manager.init_app(app)

    from app import models

    from .templates.account import account
    from .templates.main import main
    from .templates.target import target


    app.register_blueprint(main)
    app.register_blueprint(account)
    app.register_blueprint(target)

    return app

So, this solution works, and it can be applied to multiple blueprints. My concern is that I am missing some important details that necessitated other, more complex solutions. Is there a security weak point? Other issues?

References:

https://flask-login.readthedocs.io/en/latest/

How do I pass through the "next" URL with Flask and Flask-login?

Flask/Werkzeug, how to return previous page after login

Flask how to redirect to previous page after successful login

Get next URL using flask redirect

Flask Basic HTTP Auth use login page

0 Answers
Related