Fastapi OAuth2 令牌处理。缺少授权 header

Fastapi OAuth2 token handeling. Missing Authorization header

我将 FastAPI 与 OAuth2PasswordBearer 和 RequestForm 结合使用来实现用户登录。登录和检索令牌有效,但使用令牌对我不起作用。

我得到了这个 OAuth2PasswordBearer 设置和 /token 函数:


authmanager = OAuth2PasswordBearer(tokenUrl='dauPP/token')

@router.post("/token", response_model=Token)
async def login_for_access_token(db: AsyncIOMotorDatabase = Depends(get_database),
                                 form_data: OAuth2PasswordRequestForm = Depends()):
    user = await authenticate_user(db, form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.username}, expires_delta=access_token_expires
    )
    return {"access_token": access_token, "token_type": "bearer"}

此函数根据我的 MongoDB 检查表单中提供的给定用户名和密码,效果很好。它 returns 一个 access_token 和 token_type:

但是如果我尝试访问托管路由器 f.e。 /家

@router.get("/home", response_class=HTMLResponse)
async def get_home(request: Request, current_user: User = Depends(get_current_active_user)):
    return templates.TemplateResponse("home.html", {
        "request": request, "title": "[D]ocument [A]dvanced [U]tility", "subtitle": current_user.username
    })

我收到一个

"Not authenticated"

响应。这会很好...如果我没有事先获得令牌。

我检查了 PasswordBearer 正在处理什么:

    async def __call__(self, request: Request) -> Optional[str]:
        print(request.headers)
        authorization: str = request.headers.get("Authorization")
        scheme, param = get_authorization_scheme_param(authorization)
        if not authorization or scheme.lower() != "bearer":
            if self.auto_error:
                raise HTTPException(
                    status_code=HTTP_401_UNAUTHORIZED,
                    detail="Not authenticated",
                    headers={"WWW-Authenticate": "Bearer"},
                )
            else:
                return None
        return param

所以我看了一下 request.headers 的内容,这就是我发现的(“Cheking for...”是我添加的印刷品):

INFO:     127.0.0.1:59213 - "POST /dauAPP/token HTTP/1.1" 200 OK
Headers({'host': '127.0.0.1:8000', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', 'accept-language': 'de,en-US;q=0.7,en;q=0.3', 'accept-encoding': 'gzip, deflate', 'connection': 'keep-alive', 'upgrade-insecure-requests': '1', 'sec-fetch-dest': 'document', 'sec-fetch-mode': 'navigate', 'sec-fetch-site': 'none', 'sec-fetch-user': '?1'})
Checking for authorization in header...result:None
INFO:     127.0.0.1:59213 - "GET /dauAPP/home HTTP/1.1" 401 Unauthorized

我对 web-dev 很陌生,所以我的想法可能存在根本性缺陷,但我假设返回访问令牌会导致 client/browser 存储它并自动添加它对其在“授权”中的请求 headers。我必须在客户端对令牌做些什么吗?可能配置有问题,所以 Auth-Header 被切断了?

我认为它是如何工作的:

get_home is called 
async def get_home(request: Request, current_user: User = Depends(get_current_active_user)):
--> dependency calling get_current_active_user

async def get_current_active_user(current_user: User = Depends(get_current_user)):
--> dependency calling get_current_user

async def get_current_user(db: AsyncIOMotorDatabase = Depends(get_database), token: str = Depends(authmanager)):
--> dependency calls authmanager (OAuth2PasswordBearer)

which calls the def __call__ function mentioned earlier and here the header is missing.

but I would assume, that returning an access token would result in the client/browser storing it and automatically adding it to its request headers in "Authorization"

这个假设是你错的地方。您的应用程序有责任存储令牌并针对针对端点的每个请求传输令牌。如果您计划使用常规 HTML 并以用户单击链接而不是 API 端点的方式浏览它,您可能需要考虑使用 cookie 而不是 HTTP header (由您的浏览器自动发送)。

swagger-ui 可以从 API 签名确定需要授权 header(这是 OAuth2PasswordBearer 所做的,除其他外),它知道它可以要求并预计将呈现 header。由于 swagger-ui 不是作为 HTTP 一部分的通用 Web 标准,因此浏览器不应该或能够做到这一点。

不过,Cookie 确实可以达到这个目的 - 因此,如果您愿意,可以使用 cookie。但是 API 请求不包含 cookie,并且对这些请求使用授权 header 更为常见。