系统应如下:
auth_request
指令对Flask应用程序进行子请求(将请求作为标头传递给凭证),期望响应为200或401。
flask.session
的一部分。如果是这样,它会立即返回200响应;否则,它会尝试对外部事实来源进行身份验证。如果成功,则将该用户添加到flask.session
,该应用返回200响应;否则,它将返回401响应。flask.session
中删除并返回401响应。然后,用户可以发出新的登录请求。用户的浏览器应该缓存他们提交的凭据,因此每个新请求都应该能够立即看到该用户属于flask.session
并避免进行新的身份验证尝试。
看似随机,在刷新或导航受保护资源时(成功验证后),有时会出现身份验证弹出窗口,并且需要用户再次进行身份验证。他们可以提交,并在再次提示重新进行身份验证之前加载单个资源。
实施例
受保护资源是一个静态网站,由索引页,CSS文件和三个图像组成。初始身份验证后,用户会多次刷新页面。其中一次,将触发身份验证提示。他们将再次输入他们的凭证,并将加载索引页面。他们会再次提示输入CSS文件,然后再次提示每个图像。
我不确定我需要在此处链接多少内容才能解决问题,因此我将从负责制作auth_request
的nginx文件开始subrequest和后续路由,以及负责发出auth请求和处理会话的两个python文件。
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 or not check_auth(auth.username, auth.password):
return authenticate()
return f(*args, **kwargs)
return decorated