系统应该如下:
auth_request
指令向Flask应用程序发出一个子请求(在请求中传递凭证作为标题),期待200或401响应。
flask.session
一部分。 如果是这样,它立即返回200响应; 否则,它会尝试对外部真相来源进行authentication。 如果成功,则将该用户添加到flask.session
并且该应用程序返回200响应; 否则,它将返回一个401响应。 flask.session
删除,并返回401响应。 用户然后可以进行新的login请求。 用户的浏览器应该caching他们提交的证书,所以每个新的请求应该能够立即看到用户是flask.session
一部分,并避免进行新的身份validation尝试。
在随机刷新或导航受保护资源时(成功validation后),有时会出现身份validationpopup窗口,用户需要再次进行validation。 他们可以提交,单个资源将被加载,然后再次提示重新进行身份validation。
例:
受保护资源是由索引页,CSS文件和三个图像组成的静态网站。 初始authentication后,用户多次刷新页面。 其中一个时间,身份validation提示将被触发。 他们将再次input他们的凭据,索引页面将加载。 他们会再次提示CSS文件,然后再为每个图像。
我不知道有多less事情需要链接到这里来解决这个问题,所以我将从负责创buildauth_request
子请求和后续路由的nginx文件开始,并且负责创build两个python文件进行身份validation请求和处理会话。
nginx.default
server { listen 80; server_name _; location / { auth_request /auth; proxy_pass {{resource_endpoint}}; } location /auth { proxy_pass {{auth_backend}}; proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Original-URI $request_uri; } location /logout { proxy_pass {{auth_backend}}; proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Original-URI $request_uri; } }
app.py
import flask import auth0 import os app = flask.Flask(__name__) app.secret_key = os.getenv("SECRET_KEY", 'sooper seekrit') @app.route('/auth', methods=['GET']) @auth0.requires_auth def login(): print("logging in") resp_text = ( "Authentication successful." ) return flask.Response( response=resp_text, status=200, ) @app.route('/logout', methods=['GET']) def logout(): # To successfully invalidate a user, we must both clear the Flask session # as well as direct them to a '401: Unauthorized' route. # http://stackoverflow.com/questions/233507/how-to-log-out-user-from-web-site-using-basic-authentication print("logging out") flask.session.clear() return flask.Response( response='Logout', status=401, ) if __name__ == "__main__": app.debug = True from gevent.wsgi import WSGIServer http_server = WSGIServer(('', 8000), app) http_server.serve_forever()
auth0.py
import json import requests import flask import datetime import os from functools import wraps def check_auth(username, password): if 'username' in flask.session: import pdb; pdb.set_trace() if 'logged_in' in flask.session: now = datetime.datetime.now() expiry_window = datetime.timedelta( minutes=int(os.getenv('AUTH0_EXPIRY')) ) if flask.session['logged_in'] >= now - expiry_window: return True else: flask.session.pop('username', None) data = { 'client_id': os.getenv("AUTH0_CLIENT_ID"), 'username': username, 'password': password, 'id_token': '', 'connection': os.getenv("AUTH0_CONNECTION"), 'grant_type': 'password', 'scope': 'openid', 'device': '' } resp = requests.post( url="https://" + os.getenv('AUTH0_DOMAIN') + "/oauth/ro", data=json.dumps(data), headers={"Content-type": "application/json"} ) if 'error' in json.loads(resp.text): return False else: flask.session['username'] = username flask.session['logged_in'] = datetime.datetime.now() return True def authenticate(): return flask.Response( 'Could not verify your access level for that URL.\n' 'You have to login with proper credentials', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'}, ) def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): auth = flask.request.authorization if not auth: if not auth or not check_auth(auth.username, auth.password): return authenticate() return f(*args, **kwargs) return decorated