Question

I am using the Flask-login's @login_required decorator for some routes in my app. When navigating to those routes while not logged in I am redirected to the login page. So far so good.

The redirected url looks like this: /login?next=%2Fusers
Looks like it url-encoded the next parameter, something I haven't seen in the examples I've run across. After logging in the redirect back to next is always failing and falling back to the index page. I think this is because next is url-encoded.

Should I be going about this a different way? I'm just starting to dive into the Flask framework and working off of examples so I don't know much about the best ways to do things.

Here's an example route:

login_manager.login_view = 'login'

@app.route('users')  
@login_required  
def users():  
  return 'Test'

And my login route looks like this:

@app.route('/login')
def login():
  error = None
  next = request.args.get('next')
  if request.method == 'POST':
    username = request.form['username']
    password = request.form['password']

    if authenticate_user(username, password):
      user = User.query.filter_by(username=username).first()
      if login_user(user):
        flash("You have logged in")
        session['logged_in'] = True
        return redirect(next or url_for('index', error=error))
    error = "Login failed"
  return render_template('login.html', login=True, next=next, error=error)

Thanks in advance for the help.

Was it helpful?

Solution

I figured it out, navigating to a login-protected route (/requests in this example) would cause a redirect to the login page with the next parameter.

/login?next=%2Frequests  

In my login template, I had this:

<form action="{{ url_for('login') }}" method='POST'>

This caused the form to be posted to the /login route without any parameters. Removing the action attribute or changing it to action="" causes the form to be posted to its own url, which includes the original query string. Another option would be including next=next in url_for.

<form action="{{ url_for('login', next=next) }}" method='POST'>

OTHER TIPS

Here you go:

import urllib


@app.route('/login')
def login():
    error = None
    next = urllib.unquote_plus(request.args.get('next'))
    ...

In case anyone finds this question like I did, here was the implementation that worked for me. The issue was that the login's POST form was only going to /login, causing the next URL is get thrown away. Following Kevan's suggestion, if you add next=request.args.get('next')) to the login form's action, it will pass the next argument with it.

Inside my login function

@app.route('/login', methods = ['GET', 'POST'])
def login():
    next = request.args.get('next')
    if request.method == 'POST':
        ...Confirm User...
            if next:
                return redirect(next)
            else:
                return redirect('/')

Inside my login form

<form method="post" action="{{ url_for('login', next=request.args.get('next')) }}" enctype="multipart/form-data">

For security reasons, it should be considered the check for the next parameter, as suggested in

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

@app.route('/login', methods=['GET', 'POST'])
  def login():
    # Here we use a class of some kind to represent and validate our
    # client-side form data. For example, WTForms is a library that will
    # handle this for us, and we use a custom LoginForm to validate.
    form = LoginForm()
    if form.validate_on_submit():
        # Login and validate the user.
        # user should be an instance of your `User` class
       login_user(user)

       flask.flash('Logged in successfully.')

       next = flask.request.args.get('next')
       # is_safe_url should check if the url is safe for redirects.
       # See http://flask.pocoo.org/snippets/62/ for an example.
       if not is_safe_url(next):
           return flask.abort(400)

     return flask.redirect(next or flask.url_for('index'))
  return flask.render_template('login.html', form=form)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top