【发布时间】:2022-01-24 01:58:03
【问题描述】:
我没有做太多的网络工作,但我最近开始使用 FastAPI 并正在构建一个带有 jinja2 模板的 MVC 应用程序,该应用程序使用 PowerBI 嵌入式容量来服务于应用程序拥有的数据排列中的多个嵌入式分析。所有这些都非常有效。但是,我想添加更多模块,我想使用 msal 包通过将用户路由到 Microsoft 登录页面来进行用户身份验证,让他们登录我在 Azure 中设置的多租户应用程序服务,然后通过重定向 URI 重定向回我的页面,获取令牌并继续进行授权。 Microsoft 保存了一个很好的例子,我们的 here 用于在 Flask 中执行此操作。但是,我很适合将示例移植到 FastAPI。
我可以让用户进入登录屏幕并登录,但我无法在我的回调 URI 中捕获令牌 - 它是适当的路由,但我无法从响应中捕获令牌。
有没有人(或者任何人都可以)使用这个超级简单的 Flask 示例并将其移植到 FastAPI?我在网上找到的 FAPI 的所有内容都是 API 的后端令牌承载标头 - 不适用于 MVC 应用程序。
这是我当前的代码。凌乱,因为我内置了“测试”。
import msal
import requests
from fastapi import APIRouter, Request, Response
from fastapi.responses import RedirectResponse
from starlette.templating import Jinja2Templates
from config import get_settings
settings = get_settings()
router = APIRouter()
templates = Jinja2Templates('templates')
# Works
@router.get('/login', include_in_schema=False)
async def login(request: Request):
request.session['flow'] = _build_auth_code_flow(scopes=settings.AUTH_SCOPE)
login_url = request.session['flow']['auth_uri']
return templates.TemplateResponse('error.html', {'request': request, 'message': login_url})
# DOES NOT WORK - Pretty sure error is in here --------------------
@router.get('/getAToken', response_class=Response, include_in_schema=False)
async def authorize(request: Request):
try:
cache = _load_cache(request)
result = _build_msal_app(cache=cache).acquire_token_by_auth_code_flow(
request.session.get('flow'), request.session
)
if 'error' in result:
return templates.TemplateResponse('error.html', {'request': request, 'message': result})
request.session['user'] = result.get('id_token_claims')
_save_cache(cache)
except Exception as error:
return templates.TemplateResponse('error.html', {'request': request, 'message': f'{error}: {str(request.query_params)}'})
return templates.TemplateResponse('error.html', {'request': request, 'message': result})
# -----------------------------------------------------
def _load_cache(request: Request):
cache = msal.SerializableTokenCache()
if request.session.get("token_cache"):
cache.deserialize(request.session["token_cache"])
return cache
def _save_cache(request: Request, cache):
if cache.has_state_changed:
request.session["token_cache"] = cache.serialize()
def _build_msal_app(cache=None, authority=None):
return msal.ConfidentialClientApplication(
settings.CLIENT_ID,
authority=authority or settings.AUTH_AUTHORITY,
client_credential=settings.CLIENT_SECRET,
token_cache=cache
)
def _build_auth_code_flow(authority=None, scopes=None):
return _build_msal_app(authority=authority).initiate_auth_code_flow(
scopes or [],
redirect_uri=settings.AUTH_REDIRECT)
def _get_token_from_cache(scope=None):
cache = _load_cache() # This web app maintains one cache per session
cca = _build_msal_app(cache=cache)
accounts = cca.get_accounts()
if accounts: # So all account(s) belong to the current signed-in user
result = cca.acquire_token_silent(scope, account=accounts[0])
_save_cache(cache)
return result
非常感谢任何帮助。很高兴回答任何问题。谢谢。
【问题讨论】:
-
为了更清楚起见,在看似成功登录后,我在返回响应中得到“状态不匹配”。“状态不匹配:dWASFCmYrjQzXnZv vs None:code=0.AXYA4G2...... (大约还有 200 个随机字符)..."