Перенаправление на предыдущий URL-адрес после завершения входа в систему OAuth (flask-dance)

Я разрабатываю приложение Flask, которое позволяет пользователю входить в систему с помощью OAuth (с Github в качестве поставщика) и библиотеки flask-dance. По какой-то причине я не могу перенаправить после успешного входа в систему на страницу, с которой я отправил пользователя на страницу входа.

Когда пользователь пытается подключиться, например, к http: // localhost: 6675 / примеры / tutorial.first /, пользователь перенаправляется на страницу входа, показывая в URL-адресе страницу, на которую мы должны перенаправить (http: // localhost: 6675 / login? next =% 2Fexamples% 2Ftutorial.first% 2F)

Проблема в том, что после того, как мне удается войти в систему с помощью Github, приложение просто возвращается на домашнюю страницу.

Я проверял Flask-dance документация, и в документации для функции make_github_blueprint() упоминаются параметры redirect_to и redirect_url, но когда я пытаюсь их использовать, я даже не могу завершить шаг входа в систему. Более того, похоже, что он будет работать только со статическими адресами, в то время как в идеале я хотел бы вернуться на страницу, на которой был до входа в систему. Я также проверил этот ТАК вопрос, но проблема там, похоже, другая.

Есть ли примеры того, как правильно выполнять перенаправление после входа в систему с помощью Flask dance?

Вот несколько фрагментов кода, которые могут иметь отношение к делу. В файле в этом.py:

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

И в файле app.py:

@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
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
3 535
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

У меня была та же проблема, и я думаю, что то, что вы пытаетесь выполнить, на самом деле невозможно, передав следующий URL-адрес в качестве аргументов. Если вы хотите использовать аргумент «следующий» запроса, вам нужно зайти в консоль разработчика и добавить все возможные URL-адреса перенаправления, которые может иметь пользователь (что на самом деле не является хорошим решением для вас). Если вы действительно хотите сделать его динамическим, я бы посоветовал передать следующий URL-адрес в параметре состояния (см. это для получения дополнительных сведений об объекте состояния. Согласно этой документации:

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.

К сожалению, Flask-Dance не поддерживает это из коробки, поэтому вам нужно немного покопаться в библиотеке. Я бы посоветовал взглянуть на объект OAuth2ConsumerBlueprint и попытаться реализовать собственный план, который подойдет вам.

В моем случае я хотел перенаправить пользователя в другое приложение в моем домене после успешной аутентификации OAuth. Что я делаю с Flask-Dance, так это передаю URL-адрес другого приложения в параметре «следующий», например,

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

и я обязательно добавляю полный авторизованный URL-адрес в консоль Google, например:

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

С помощью этого метода вы можете перенаправить на желаемые URL-адреса без использования параметра redirect_to (который может принимать только одно значение) в flask-dance.

Большое спасибо за подробный ответ! В конце концов моя компания решила разрешить только пользователям, зарегистрированным в нашем домене, поэтому в конце концов не потребовался вход OAuth: /

Fasteno 09.09.2018 19:13

Если ваша компания использует Google для управления пользователями и электронной почтой (GSuite), очень просто использовать Flask-Dance с Google OAuth2.0 и добавить условие для регистрации пользователя, чтобы убедиться, что он является частью домена компании. В разрешенном сигнале OAUTH вы можете сделать что-то вроде: если "hd" в google_info и google_info ["hd"] == "YOUR_COMPANY_DOMAIN.com": а затем авторизовать пользователя. Это упрощает вход для пользователей.

Jean-Michel Provencher 10.09.2018 14:23

Если он ответит на ваш вопрос, не забудьте проголосовать за @Fasteno

Jean-Michel Provencher 12.09.2018 17:53

Я принял ваш ответ, хотя он не совсем соответствовал тому, что я спросил. Я подумал, что, вероятно, будет лучше просто проголосовать, но с моей низкой репутацией, похоже, я не могу этого сделать. Надеюсь, это не имеет большого значения.

Fasteno 13.09.2018 08:51

Мой ответ может быть немного запоздалым, поскольку вы упомянули, что ваша компания решила разрешить пользователям регистрироваться только в вашем домене. Однако я все еще отправляю это другим пользователям, у которых может быть этот вопрос.

Чтобы решить эту проблему, вам понадобится базовая страница входа, которая сначала хранит next_url, на который пользователь должен быть перенаправлен после завершения входа в систему с помощью входа в домен или входа в систему OAuth. Flask добавляет параметр next в URL-адрес, когда вы используете @login_required в URL-адресе, который должен быть разрешен только для зарегистрированных пользователей. Вам необходимо добавить session['next_url'] = request.args.get('next') в свой маршрут входа, чтобы захватить параметр next и сохранить его в сеансе как next_url. Теперь, в случае входа в систему OAuth, как только пользователь будет перенаправлен на redirect_url по умолчанию после успешного входа в систему, вы прочитаете значение next_url из сеанса и перенаправите пользователя на этот URL-адрес, если он существует, или отобразите страницу по умолчанию (обычно домашняя страница.)

Я предполагаю 2 вещи: 1. Ваш URL-адрес по умолчанию после успешного входа в систему OAuth - /home. Это устанавливается с помощью redirect_url функции make_provider_blueprint, в вашем случае это make_github_blueprint. Во-вторых, я предполагаю, что ваша страница входа является целевой страницей для пользователя, когда он не вошел в систему, и на ней отображается кнопка входа в GitHub, которая запускает процедуру входа в систему OAuth. Возможно, вам придется немного изменить этот поток для вашего случая, чтобы соответствующим образом захватить next_url.

Ниже я поделился минимальным кодом, необходимым для обработки этой ситуации. Обратите внимание, что я извлекаю значение next_url из сеанса перед перенаправлением. Это сделано для того, чтобы в случае, если пользователь пытается получить доступ к домашней странице после входа в систему, его не всегда следует перенаправлять на next_url, если он существует в сеансе.

bp_github = make_github_blueprint(
    client_id = "...",
    client_secret = "...",
    redirect_url = "/home"
)

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

@auth.route("/")
@auth.route("/home")
@login_required
def home():
    if session.get('next_url'):
        next_url = session.get('next_url')
        session.pop('next_url', None)
        return redirect(next_url)
    return render_template('home.html')

Сообщите мне, было ли это полезно или вам нужна другая информация. Буду рад помочь.

Спасибо за ответ. Его можно использовать в качестве справочника в будущем.

Fasteno 25.11.2018 16:37

Цените этот ответ, спасибо.

Ethan Zerad 19.06.2021 14:23

Я знаю, что это старый вопрос, но я просто хотел бы рассказать, как я преодолеваю эту проблему с помощью flask-dance и перенаправления oauth2.

Я предлагаю вам сохранить следующую страницу в переменной сеанса вот так.

Ниже приведен ваш танцевальный маршрут фляги для аутентификации, здесь вы можете захватить ссылающуюся страницу и сохранить ее в переменной сеанса.

@main.route("/googleLogin")
def googleLogin():
    # here you store the page the user has come from with the referrer value
    session["next"] = request.referrer  


    # then do your login stuff
    if not google.authorized:
        return redirect(url_for("google.login")):
    else:
        return redirect(request.referrer, code=302)  #redirect if needed

@main.route("/yourAuthorisedLoginRoute") # probably / or /home
def authed_route():
    #you can add this section of code to 1) grab the next url if 
    # its there. If it is then destroy it so the user 
    #isn't redirect there next time they navigate to 
    #this page. or 2) skip if the next value doesn't exist.

    redirecter = session.get("next",None)
    if not redirecter is None:
        session.pop("next")
        return redirect(redirecter, code=302)

    #HERE IS YOUR NORMAL PAGE CODE.
    #i.e 
    return render_template("home.html")

Недавно у меня была аналогичная проблема, я наткнулся на этот вопрос и подумал, что опубликую свое решение, чтобы другие могли получить пользу. В других ответах есть много хороших объяснений того, что нужно сделать, чтобы решить эту проблему, поэтому я не буду повторять детали. Основная идея решения та же: сохранить текущий маршрут и затем перенаправить обратно на него после успешной авторизации.

Сервер авторизации в моем примере был внутренним для моей компании, но заменить OAuth2ConsumerBlueprint на поставщика по вашему выбору не составит труда.

from flask import Flask, redirect, url_for, session, redirect, request
from flask_dance.consumer import OAuth2ConsumerBlueprint, oauth_authorized
from functools import wraps

app = Flask(__name__)

oauth_blueprint = OAuth2ConsumerBlueprint(
    "oauth",
    __name__,
    client_id = "W7n...JD6",
    client_secret = "QoU...pXEX",
    base_url='https://the.oauth.server.com',
    token_url='https://the.oauth.server.com/token/',
    authorization_url='https://the.oauth.server.com/authorize/',
    scope = "openid profile",
)
app.register_blueprint(oauth_blueprint, url_prefix = "/login")


def auth_required(func):
    @wraps(func)
    def check_authorization(*args, **kwargs):
        """check if authorized or token expired and authenticate if necessary"""

        token = oauth_blueprint.session.token
        if not oauth_blueprint.session.authorized or token["expires_in"] < 0:
            # store current route before redirecting so we can return after successful auth
            session["next_url"] = request.path
            return redirect(url_for("oauth.login"))
        return func(*args, **kwargs)

    return check_authorization


@oauth_authorized.connect
def redirect_to_next_url(blueprint, token):
    """
    allows the the callback url to be dynamic without having to register
    each protected endpoint as a redirect with OAuth2ConsumerBlueprint.
    """
    blueprint.token = token
    next_url = session["next_url"]
    return redirect(next_url)


@app.route('/my_protected_resource')
@auth_required
def my_protected_resource():
    token = oauth_blueprint.session.token

    # add your endpoint's logic here ...
    # decode token['id_token'] for user information
    # use token['access_token'] to access protected resources
    return

Другие вопросы по теме