如何从FastAPI中的重定向URL获取#后面的查询参数?

问题描述 投票:0回答:1

我在使用 Redirect Url 并使用 FastApi 从 Python 中的 QueryParams 获取数据时遇到问题。我正在使用 Azure AD 授权授予流程登录,下面是生成

RedirectResponse

的代码
@app.get("/auth/oauth/{provider_id}")
async def oauth_login(provider_id: str, request: Request):
    if config.code.oauth_callback is None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No oauth_callback defined",
        )

    provider = get_oauth_provider(provider_id)
    if not provider:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Provider {provider_id} not found",
        )

    random = random_secret(32)

    params = urllib.parse.urlencode(
        {
            "client_id": provider.client_id,
            "redirect_uri": f"{get_user_facing_url(request.url)}/callback",
            "state": random,
            **provider.authorize_params,
        }
    )
    response = RedirectResponse(
        url=f"{provider.authorize_url}?{params}")
    samesite = os.environ.get("CHAINLIT_COOKIE_SAMESITE", "lax")  # type: Any
    secure = samesite.lower() == "none"
    response.set_cookie(
        "oauth_state",
        random,
        httponly=True,
        samesite=samesite,
        secure=secure,
        max_age=3 * 60,
    )
    return response

这就是我接收重定向 URL 的地方。

@app.get("/auth/oauth/{provider_id}/callback")
async def oauth_callback(
    provider_id: str,
    request: Request,
    error: Optional[str] = None,
    code: Optional[str] = None,
    state: Optional[str] = None,
):
    if config.code.oauth_callback is None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No oauth_callback defined",
        )
    provider = get_oauth_provider(provider_id)
    if not provider:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Provider {provider_id} not found",
        )


    
    if not code or not state:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Missing code or state",
        )
    response.delete_cookie("oauth_state")
    return response

当 QueryParams 带有 ? 时,此重定向工作正常。但现在的问题是,来自 Azure AD 的重定向回调带有 #,因此我无法从 Url

 获取 
Code
State

QueryParams

带有
#

的 RedirectUrl 示例

http://localhost/callback#code=xxxxxx&state=yyyyyy

关于如何解决此问题的任何想法。

python http azure-active-directory fastapi query-string
1个回答
0
投票

获取服务器端散列标记

#
之后的文本(或键值对,在您的情况下)— URL 中的
#
也称为 URI 片段(请参阅 文本片段) MDN 文档也是如此)——目前不可能。这只是因为片段从不发送到服务器(相关帖子可以在这里这里找到)。

我建议在 URL 中使用 问号

?
,这是在 HTTP 请求中发送查询参数的正确方法。如果这是一个复杂的路径参数,您可以按照这个答案中描述的方法进行操作,这将允许您捕获整个 URL 路径,包括
/
%
等字符,但仍然不是文本/值
#
之后。

更新

由于“片段”仅在客户端可用/可访问,因此您可以使用 JavaScript 来获取

hash
接口的
Location
属性,即
window.location.hash
。为此,您可以有一个
/callback_init
端点,该端点最初被调用并用作授权服务器的
redirect_uri
,它将返回相关的 JavaScript 代码来读取片段并将其传递到 URL 的查询字符串中到最后的
/callback
端点。这可以通过将 URL 中的
#
替换为
?
轻松完成,如下所示:

@app.get('/callback_init', response_class=HTMLResponse)
async def callback_init(request: Request):
    html_content = """
    <html>
       <head>
          <script>
             var url = window.location.href;
             newUrl = url.replace('/callback_init', '/callback').replace("#", "?");
             window.location.replace(newUrl);
          </script>
       </head>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)

但是,这种方法不会考虑 URL 中已经存在的查询参数的可能性(即使这在您的情况下不是问题);因此,您可以使用以下内容。

下面的示例还考虑到您设置了 cookie,您需要稍后将其删除;因此,这也在下面得到证明。另请注意,为了替换 URL(或向

/callback
端点发送请求),使用
window.location.replace()
,正如 this answer 中所述,不会让当前页面(在导航到下一个)保存在会话历史记录中,这意味着用户将无法使用浏览器中的后退按钮导航回该位置(如果出于某种原因,您必须允许用户返回,则您可以使用
window.location.href
window.location.assign()
代替)。

最后,请注意,在查询字符串中发送敏感信息是不安全&mdashl请参阅此答案了解有关该主题的更多详细信息。

工作示例

要触发重定向,请转到浏览器并拨打

http://localhost:8000/

from fastapi import FastAPI, Request, Response
from fastapi.responses import RedirectResponse, HTMLResponse
from typing import Optional


app = FastAPI()


@app.get("/")
async def main():
    redirect_url = 'http://localhost:8000/callback_init?msg=Hello#code=1111&state=2222'
    response = RedirectResponse(redirect_url)
    response.set_cookie(key='some-cookie', value='some-cookie-value', httponly=True)
    return response
    
    
@app.get('/callback_init', response_class=HTMLResponse)
async def callback_init(request: Request):
    html_content = """
    <html>
       <head>
          <script>
             var url = window.location.href;
             const fragment = window.location.hash;
             const searchParams = new URLSearchParams(fragment.substring(1));
             url = url.replace('/callback_init', '/callback').replace(fragment, "");
             const newUrl = new URL(url);            
             for (const [key, value] of searchParams) {
                newUrl.searchParams.append(key, value);
            }
             window.location.replace(newUrl);
          </script>
       </head>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)


@app.get("/callback")
async def callback(
    request: Request,
    response: Response,
    code: Optional[str] = None,
    state: Optional[str] = None,
):
    print(request.url.query)
    print(request.cookies)
    response.delete_cookie("some-cookie")
    return {"code": code, "state": state}
© www.soinside.com 2019 - 2024. All rights reserved.