pythonflaskoauthflask-loginflask-oauthlib

Redirect to previous URL after OAuth login is completed (flask-dance)


I am developing a Flask application that allows the user to login using OAuth (with Github as a provider), and the flask-dance library. For some reason I am not able to redirect, after a successful login, to the page from which I sent the user to the login page.

When the user tries to connect to, e.g., http://localhost:6675/examples/tutorial.first/, the user is redirected to the login page, showing in the URL the page we should redirect to (http://localhost:6675/login?next=%2Fexamples%2Ftutorial.first%2F)

The problem is that after I manage to login using Github, the application just goes back to the homepage.

I was checking Flask-dance documentation and the documentation for the make_github_blueprint() function mentions the parameters redirect_to and redirect_url, but when I try using them I cannot even complete the login step. Furthermore, it seems that it would work only with static addresses, while ideally I would like to jump back to the page I was before logging in. I also checked this SO question, but the problem there seems to be different.

Are there any examples on how to properly do redirection after logging in with Flask dance?

Here some code snippets which could be relevant. In the init.py file:

bp_github = make_github_blueprint(
    client_id="...",
    client_secret="...",
)
login_manager = LoginManager()
login_manager.login_github_view = 'github.login'
login_manager.login_view = 'login'

And in the app.py file:

@app.route("/login", methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return flask.redirect(flask.url_for('/'))
    return flask.render_template('login.html')

@app.route("/logout")
@login_required
def logout():
    logout_user()
    flask.flash("You have logged out")
    return flask.redirect(flask.url_for("login"))

@oauth_authorized.connect_via(bp_github)
def logged_in(blueprint, token):
    """
    create/login local user on successful OAuth login with github
    :param blueprint:
    :param token:
    :return:
    """
    if not token:
        flask.flash("Failed to log in.", category="error")
        return False

    session = blueprint.session

    resp = session.get("/user")

    if not resp.ok:
        msg = "Failed to fetch user info."
        flask.flash(msg, category="error")
        return False

    user_id = str(info["id"])

    # Find this OAuth token in the database, or create it
    query = OAuth.query.filter_by(
        provider=blueprint.name,
        provider_user_id=user_id,
    )
    try:
        oauth = query.one()
    except NoResultFound:
        oauth = OAuth(
            provider=blueprint.name,
            provider_user_id=user_id,
            token=token,
        )

    if oauth.user:
        login_user(oauth.user)
        flask.flash("Successfully signed in.")

    else:
        # Create a new local user account for this user
        name = info['login']

        user = User(
            email=info["email"],
            name=name,
            provider=provider
        )
        # Associate the new local user account with the OAuth token
        oauth.user = user
        # Save and commit our database models
        db.session.add_all([user, oauth])
        db.session.commit()
        # Log in the new local user account
        login_user(user)
        flask.flash("Successfully signed in.")

    # Disable Flask-Dance's default behavior for saving the OAuth token
    return False

Solution

  • I had the same problem and I think what you are trying to accomplish is not really possible by passing the next url as arguments. If you want to use the "next" request argument, you need to go in the developer console and add every possible redirect url that the user could have (which is not really a good solution for you). What I would suggest if you really want to make it dynamic is to pass the next url in the state parameter (see this for more details about the state object. According to this documentation:

    You can use this parameter for several purposes, such as directing the user to the correct resource in your application, sending nonces, and mitigating cross-site request forgery.

    Unfortunately, Flask-Dance does not support this out the box so you will need to dig a little bit in the library. I would suggest taking a look at the OAuth2ConsumerBlueprint object and try to implement a custom blueprint that would work for you.

    In my case, I wanted to redirect the user to another application on my domain after the successful OAuth authentication. What I do with Flask-Dance is pass the other application url in the "next" parameter like so

    http://api.mycompany.com/auth/google?next=https://myotherapp.mycompany.com
    

    and I make sure to add the complete authorized url in the google console like such:

    http://api.mycompany.com/auth/google/authorized?next=https%3A%2F%2Fmyotherapp.mycompany.com
    

    With this technique you can redirect to the desired urls without using the redirect_to parameter (which can only take one value) in flask-dance