我想使用@oidc.require_login
将登录请求重定向到okta。我收到无法解决的AttributeError: '_AppCtxGlobals' object has no attribute 'oidc_id_token'
错误
1)我基于Flaskr教程构建了一个应用,该应用程序演示了烧瓶工厂方法create_app
的使用。
2)我创建了一个okta.py
类,如下所示:
from oauth2client.client import OAuth2Credentials
from flask_oidc import OpenIDConnect
from okta import UsersClient
import click
from flask import current_app, g, session
from flask.cli import with_appcontext
oidc = OpenIDConnect()
def get_oidc():
"""
Connect to okta
"""
if 'oidc' not in g:
print('okta: get_oidc call')
g.oidc = OpenIDConnect(current_app)
g.okta_client = UsersClient("<okta-server-url>", "<secret>")
# fixing global oidc problem for decorator in rooms
oidc = g.oidc
return g.oidc
def close_oidc(app):
""" Release okta connection
"""
oidc = g.pop('oidc',None)
if oidc is not None:
oidc.logout()
# with app.app_context():
# session.clear()
def init_okta():
"""Connect to existing table"""
oidc = get_oidc()
""" Can do additional initialization if required """
@click.command('init-okta')
@with_appcontext
def init_okta_command():
"""Connect to existing oidc"""
init_okta()
click.echo (get_oidc())
click.echo('Initialized Okta.')
print('Initialized Okta.')
def init_app(app):
"""Register okta functions with the Flask app. This is called by
the application factory.
"""
app.teardown_appcontext(close_oidc)
app.cli.add_command(init_okta_command)
3)我的目标是使用okta登录浏览房间列表
from flask import (
Blueprint, flash, g, redirect, render_template,
request, url_for, current_app, session, jsonify
)
from werkzeug.exceptions import abort
...
from hotel.okta import oidc, get_oidc, init_app
bp = Blueprint('rooms', __name__)
...
@bp.route('/login', methods=['GET', 'POST'])
@oidc.require_login
def login():
"""
Force the user to login, then redirect them to the get_books.
Currently this code DOES NOT work
Problem:
* oidc global object is not available to pass request to okta
Resolution:
* redirecting to rooms.calendar
"""
# info = oidc.user_getinfo(['preferred_username', 'email', 'sub'])
# id_token = OAuth2Credentials.from_json(oidc.credentials_store[info.get('sub')]).token_response['id_token']
return redirect(url_for("rooms.calendar"))
4)我的__init__.py
看起来像这样
import os
from flask import Flask
from flask_oidc import OpenIDConnect
from okta import UsersClient
# This is a factory method for productive deployment
# Use app specific configuration
# For any app local files, use /instnce Folder
def create_app(test_config=None):
"""Create and configure an instance of the Flask application."""
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
# a default secret that should be overridden by instance config
SECRET_KEY='dev',
# store the database in the instance folder
DATABASE=os.path.join(app.instance_path, 'hotel.sqlite'),
OIDC_CLIENT_SECRETS=os.path.join(app.instance_path, 'client_secrets.json'),
OIDC_COOKIE_SECURE=False,
OIDC_CALLBACK_ROUTE= '/oidc/callback',
OIDC_SCOPES=["openid", "email", "profile"],
OIDC_ID_TOKEN_COOKIE_NAME = 'oidc_token',
)
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile('config.py', silent=True)
else:
# load the test config if passed in
app.config.update(test_config)
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
# # register the database commands
from hotel import db
db.init_app(app)
# apply the blueprints to the app
from hotel import rooms
app.register_blueprint(rooms.bp)
# for Okta
# Ref: https://www.fullstackpython.com/blog/add-user-authentication-flask-apps-okta.html
from hotel import okta
with app.app_context():
okta.init_app(app)
@app.route('/hello') # For testing factory method
def hello():
return 'Hello, World!'
# make url_for('index') == url_for('blog.index')
# in another app, you might define a separate main index here with
# app.route, while giving the blog blueprint a url_prefix, but for
# the tutorial the blog will be the main index
app.add_url_rule('/', endpoint='index')
return app
5)这是rooms.before_request
@bp.before_request
def before_request():
print ('rooms.before_request call reached')
with current_app.app_context():
print ('rooms.before_request in app_context',g)
oidc = g.pop('oidc',None)
okta_client = g.pop('okta_client',None)
if oidc is not None and okta_client is not None:
print ('rooms.before_request g.oidc and g.okta_client available')
if oidc.user_loggedin:
# OpenID Token as
g.user = okta_client.get_user(oidc.user_getfield("sub"))
g.oidc_id_token = OAuth2Credentials.from_json(g.oidc.credentials_store[info.get('sub')]).token_response['id_token']
else:
g.user = None
else:
print('rooms.beforerequest No user logged in')
g.user = None
我的分析:
okta.init_app(app)
应该使用client_secrets.json
文件夹中的/instance
连接到okta。 @oidc.require_login
工作,我在oidc
中暴露了okta.get_oidc
并将其导入到Rooms.py代码中File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 2328, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 2314, in wsgi_app
response = self.handle_exception(e)
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1760, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/_compat.py", line 36, in reraise
raise value
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 2311, in wsgi_app
response = self.full_dispatch_request()
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1834, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1737, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/_compat.py", line 36, in reraise
raise value
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1832, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask/app.py", line 1818, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/flask_oidc/__init__.py", line 485, in decorated
if g.oidc_id_token is None:
File "/Users/athur/Code/cmpe272WarriorsHotel/Hotel/venv/lib/python3.7/site-packages/werkzeug/local.py", line 348, in __getattr__
return getattr(self._get_current_object(), name)
AttributeError: '_AppCtxGlobals' object has no attribute 'oidc_id_token'
我的问题:
@oidc.require_login
方法?g
中设置before_request
似乎无效。 Flask的App Factory方法中还有哪些其他选项可以使用?任何见解将不胜感激!
谢谢! 尤瓦