Я разрабатываю приложение 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
У меня была та же проблема, и я думаю, что то, что вы пытаетесь выполнить, на самом деле невозможно, передав следующий 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.
Если ваша компания использует Google для управления пользователями и электронной почтой (GSuite), очень просто использовать Flask-Dance с Google OAuth2.0 и добавить условие для регистрации пользователя, чтобы убедиться, что он является частью домена компании. В разрешенном сигнале OAUTH вы можете сделать что-то вроде: если "hd" в google_info и google_info ["hd"] == "YOUR_COMPANY_DOMAIN.com": а затем авторизовать пользователя. Это упрощает вход для пользователей.
Если он ответит на ваш вопрос, не забудьте проголосовать за @Fasteno
Я принял ваш ответ, хотя он не совсем соответствовал тому, что я спросил. Я подумал, что, вероятно, будет лучше просто проголосовать, но с моей низкой репутацией, похоже, я не могу этого сделать. Надеюсь, это не имеет большого значения.
Мой ответ может быть немного запоздалым, поскольку вы упомянули, что ваша компания решила разрешить пользователям регистрироваться только в вашем домене. Однако я все еще отправляю это другим пользователям, у которых может быть этот вопрос.
Чтобы решить эту проблему, вам понадобится базовая страница входа, которая сначала хранит 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')
Сообщите мне, было ли это полезно или вам нужна другая информация. Буду рад помочь.
Спасибо за ответ. Его можно использовать в качестве справочника в будущем.
Цените этот ответ, спасибо.
Я знаю, что это старый вопрос, но я просто хотел бы рассказать, как я преодолеваю эту проблему с помощью 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
Большое спасибо за подробный ответ! В конце концов моя компания решила разрешить только пользователям, зарегистрированным в нашем домене, поэтому в конце концов не потребовался вход OAuth: /