ada*_*ton 2 python flask docker authlib
这让我非常疯狂,并阻止我进行本地开发/测试。
我有一个使用 authlib(仅限客户端功能)的烧瓶应用程序。当用户点击我的主页时,我的 Flask 后端会将他们重定向到 /login,然后又重定向到 Google Auth。Google Auth 然后将它们发回我的应用程序的 /auth 端点。
几个月来,我一直在遇到 authlib.integrations.base_client.errors.MismatchingStateError: mismatching_state: CSRF 警告的临时问题!状态在请求和响应中不相等。感觉就像一个 cookie 问题,大多数时候,我只是打开一个新的浏览器窗口或隐身或尝试清除缓存,最终,它有点工作。
但是,我现在在 docker 容器内运行完全相同的应用程序,并且在一个阶段这是有效的。我不知道我做了什么改变,但是每当我浏览到 localhost/ 或 127.0.0.1/ 并通过身份验证过程(每次清除 cookie 以确保我没有自动登录)时,我经常被重定向回 localhost /auth?state=blah blah blah 我遇到了这个问题:authlib.integrations.base_client.errors.MismatchingStateError: mismatching_state: CSRF 警告!状态在请求和响应中不相等。
我认为我的代码的相关部分是:
@app.route("/", defaults={"path": ""})
@app.route("/<path:path>")
def catch_all(path: str) -> Union[flask.Response, werkzeug.Response]:
if flask.session.get("user"):
return app.send_static_file("index.html")
return flask.redirect("/login")
@app.route("/auth")
def auth() -> Union[Tuple[str, int], werkzeug.Response]:
token = oauth.google.authorize_access_token()
user = oauth.google.parse_id_token(token)
flask.session["user"] = user
return flask.redirect("/")
@app.route("/login")
def login() -> werkzeug.Response:
return oauth.google.authorize_redirect(flask.url_for("auth", _external=True))
Run Code Online (Sandbox Code Playgroud)
我将不胜感激任何帮助。
当我在本地运行时,我从:
export FLASK_APP=foo && flask run
Run Code Online (Sandbox Code Playgroud)
当我在 docker 容器中运行时,我从:
.venv/bin/gunicorn -b :8080 --workers 16 foo
Run Code Online (Sandbox Code Playgroud)
问题是 SECRET_KEY 是使用 os.random 填充的,这为不同的工作人员产生了不同的值,因此无法访问会话 cookie。
安装旧版本的 authlib 它可以与 fastapi 和 Flask 一起正常工作
Authlib==0.14.3
Run Code Online (Sandbox Code Playgroud)
uvicorn==0.11.8
starlette==0.13.6
Authlib==0.14.3
fastapi==0.61.1
Run Code Online (Sandbox Code Playgroud)
https://dev.to/rajshirolkar/fastapi-over-https-for-development-on-windows-2p7d
ssl_keyfile="./localhost+2-key.pem" ,
ssl_certfile= "./localhost+2.pem"
Run Code Online (Sandbox Code Playgroud)
from typing import Optional
from fastapi import FastAPI, Depends, HTTPException
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.openapi.utils import get_openapi
from starlette.config import Config
from starlette.requests import Request
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import HTMLResponse, JSONResponse, RedirectResponse
from authlib.integrations.starlette_client import OAuth
# Initialize FastAPI
app = FastAPI(docs_url=None, redoc_url=None)
app.add_middleware(SessionMiddleware, secret_key='!secret')
@app.get('/')
async def home(request: Request):
# Try to get the user
user = request.session.get('user')
if user is not None:
email = user['email']
html = (
f'<pre>Email: {email}</pre><br>'
'<a href="/docs">documentation</a><br>'
'<a href="/logout">logout</a>'
)
return HTMLResponse(html)
# Show the login link
return HTMLResponse('<a href="/login">login</a>')
# --- Google OAuth ---
# Initialize our OAuth instance from the client ID and client secret specified in our .env file
config = Config('.env')
oauth = OAuth(config)
CONF_URL = 'https://accounts.google.com/.well-known/openid-configuration'
oauth.register(
name='google',
server_metadata_url=CONF_URL,
client_kwargs={
'scope': 'openid email profile'
}
)
@app.get('/login', tags=['authentication']) # Tag it as "authentication" for our docs
async def login(request: Request):
# Redirect Google OAuth back to our application
redirect_uri = request.url_for('auth')
print(redirect_uri)
return await oauth.google.authorize_redirect(request, redirect_uri)
@app.route('/auth/google')
async def auth(request: Request):
# Perform Google OAuth
token = await oauth.google.authorize_access_token(request)
user = await oauth.google.parse_id_token(request, token)
# Save the user
request.session['user'] = dict(user)
return RedirectResponse(url='/')
@app.get('/logout', tags=['authentication']) # Tag it as "authentication" for our docs
async def logout(request: Request):
# Remove the user
request.session.pop('user', None)
return RedirectResponse(url='/')
# --- Dependencies ---
# Try to get the logged in user
async def get_user(request: Request) -> Optional[dict]:
user = request.session.get('user')
if user is not None:
return user
else:
raise HTTPException(status_code=403, detail='Could not validate credentials.')
return None
# --- Documentation ---
@app.route('/openapi.json')
async def get_open_api_endpoint(request: Request, user: Optional[dict] = Depends(get_user)): # This dependency protects our endpoint!
response = JSONResponse(get_openapi(title='FastAPI', version=1, routes=app.routes))
return response
@app.get('/docs', tags=['documentation']) # Tag it as "documentation" for our docs
async def get_documentation(request: Request, user: Optional[dict] = Depends(get_user)): # This dependency protects our endpoint!
response = get_swagger_ui_html(openapi_url='/openapi.json', title='Documentation')
return response
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, port=8000,
log_level='debug',
ssl_keyfile="./localhost+2-key.pem" ,
ssl_certfile= "./localhost+2.pem"
)
Run Code Online (Sandbox Code Playgroud)
GOOGLE_CLIENT_ID=""
GOOGLE_CLIENT_SECRET=""
Run Code Online (Sandbox Code Playgroud)
@adamcunnington 这是调试它的方法:
@app.route("/auth")
def auth() -> Union[Tuple[str, int], werkzeug.Response]:
# Check these two values
print(flask.request.args.get('state'), flask.session.get('_google_authlib_state_'))
token = oauth.google.authorize_access_token()
user = oauth.google.parse_id_token(token)
flask.session["user"] = user
return flask.redirect("/")
Run Code Online (Sandbox Code Playgroud)
检查输入的值request.args并session查看发生了什么情况。
也许是因为Flask 会话在 Heroku 上使用 Gunicorn 的 Flask 应用程序中的请求之间不持久
| 归档时间: |
|
| 查看次数: |
4586 次 |
| 最近记录: |