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

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


If you need to specify a different action attribute in your form you can't use the next parameter provided by Flask-Login. I'd recommend anyways to put the endpoint instead of the url into the url parameter since it is easier to validate. Here's some code from the application I'm working on, maybe this can help you.

Overwrite Flask-Login's unauthorized handler to use the endpoint instead of the url in the next parameter:

@login_manager.unauthorized_handlerdef handle_needs_login():    flash("You have to be logged in to access this page.")    return redirect(url_for('account.login', next=request.endpoint))

Use request.endpoint in your own URLs too:

{# login form #}<form action="{{ url_for('account.login', next=request.endpoint) }}" method="post">...</form>

Redirect to the endpoint in the next parameter if it exists and is valid, else redirect to a fallback.

def redirect_dest(fallback):    dest = request.args.get('next')    try:        dest_url = url_for(dest)    except:        return redirect(fallback)    return redirect(dest_url)@app.route("/login", methods=["GET", "POST"])def login():    ...    if user_is_logged_in:        flash("Logged in!")        return redirect_dest(fallback=url_for('general.index'))    else:        flash("Sorry, but you could not log in.")        return render_template("login.html")


@timakro provides a neat solution.If you want to handle a dynamic link such as

index/<user>

then using url_for(request.endpoint,**request.view_args) instead because request.endpoint will not contain the dynamic vairable info:

 @login_manager.unauthorized_handler def handle_needs_login():     flash("You have to be logged in to access this page.")     #instead of using request.path to prevent Open Redirect Vulnerability      next=url_for(request.endpoint,**request.view_args)     return redirect(url_for('account.login', next=next))

the following code shall be changed to:

def redirect_dest(home):    dest_url = request.args.get('next')    if not dest_url:        dest_url = url_for(home)    return redirect(dest_url)@app.route("/login", methods=["GET", "POST"])def login():    ...    if user_is_logged_in:        flash("Logged in!")        return redirect_dest(home=anyViewFunctionYouWantToSendUser)    else:        flash("Sorry, but you could not log in.")        return render_template("login.html")


Just in case someone is trying to pass through the "next" URL with Flask-Login but with Flask_Restful, the workaround I've been using is passing the "next" argument from the GET method to the POST method. With flask_restful the "next_page" argument is set to "None" after clicking on the Login Button in the login.html

login.html

...<!-- next_page came from "render_template(next_page=request.args.get('next') ...)" in the get() function --><!-- And also from render_template('login.html', next_page=next_page) in the post() function --><form action="{{ url_for('login', next=next_page) }}" method="POST" >    <div class="field">        <div class="control">            <input class="input is-large" type="email" name="email" placeholder="Your Email" autofocus="">        </div>    </div>    <div class="field">        <div class="control">            <input class="input is-large" type="password" name="password" placeholder="Your Password">        </div>    </div>    <div class="field">        <label class="checkbox">            <input type="checkbox" name="remember_me">            Remember me        </label>    </div>    <button class="button is-block is-info is-large is-fullwidth">Login</button></form>...

auth.py

class Login(Resource):    def get(self):        if current_user.is_authenticated:            return redirect(url_for('home'))        headers = {'Content-Type': 'text/html'}#1 -->  # Here I pass the "next_page" to login.html        return make_response(render_template('login.html', next_page=request.args.get('next')), 200, headers)    def post(self):#2 -->  # After the user clicks the login button, I retrieve the next_page saved in the GET method        next_page = request.args.get('next')        if current_user.is_authenticated:            return redirect(url_for('home'))        # Check if account exists in the db        existing_account = Account.objects(email=request.form.get('email')).first()        # Only redirects when the URL is relative, which ensures that the redirect         # stays within the same site as the application.        if existing_account:            if existing_account.check_password(request.form.get('password')):                login_user(existing_account, remember=request.form.get('remember_me'))                if not next_page or url_parse(next_page).netloc != '':                    return redirect(url_for('home'))#3 -->          # Here I use the retrieved next_page argument                return redirect(url_for(next_page))        # Account not recognized        flash('Please check your login details and try again.')        headers = {'Content-Type': 'text/html'}#4 -->  # I also pass the "next_page" here in case the user-provided data is wrong        return make_response(render_template('login.html', next_page=next_page), 200, headers)