如何验证在 FastAPI 中作为 HttpOnly cookie 发送的 JWT?

snk*_*y21 2 fetch oauth-2.0 jwt cookie-httponly fastapi

问题

我正在开发一个 FastAPI 应用程序,该应用程序需要对用户访问的某些端点进行身份验证。我正在使用 FastAPI 中的 Oauth2 和 Jose 为我的身份验证过程创建 JWT。经过一些研究后,确保令牌在前端受到保护的最佳方法似乎是将它们存储在 HttpOnly Cookie 中。我正在努力了解如何通过 HttpOnly Cookie 正确传递 JWT,以便我的 FastAPI 服务器能够验证标头中的 JWT。目前,当我尝试将 JWT 令牌作为 HttpOnly Cookie 传递时,我得到一个401 Unauthorized Error.

我尝试过的

当我将令牌作为模板字符串编码到标头中时,我已经能够使用 JWT 令牌成功地对用户进行身份验证。但是,当我通过标头将 JWT 作为 Cookie 传递到 FastAPI 服务器时,我的 FastAPI 服务器无法对用户进行身份验证并返回401 unauthorized error. 我尝试查看网络选项卡,看看我的请求中向 FastApi 服务器发送了哪些标头,以便更好地了解这两种情况之间的不同之处。

成功示例及代码

当我将 JWT 作为模板字符串传递并获得 200 响应时,这是在标头中:

身份验证:不记名令牌

  async function getPosts() {
    const url = "http://localhost:8000/posts";
    const fetchConfig = {
      headers: {
        Authorization: `Bearer ${tokenValue}`,
      },
    };
    const response = await fetch(url, fetchConfig);
    const posts = await response.json();
  }
Run Code Online (Sandbox Code Playgroud)

不成功的示例代码

当我将 JWT 作为 HttpOnly Cookie 传递并获得 401 响应时,这是在标头中:

Cookie:access_token =“承载令牌”

我还尝试更改在服务器上设置 cookie 的方式,以便标头如下所示:

Cookie:身份验证=“承载令牌”

  async function getPosts() {
    const url = "http://localhost:8000/posts";
    const fetchConfig = {
      credentials: "include",
    };
    const response = await fetch(url, fetchConfig);
    const posts = await response.json();
    console.log(posts);
  }
Run Code Online (Sandbox Code Playgroud)

FastAPI代码

以下是用于保护我的 API 端点的 Oauth2 令牌验证的代码。这是基于 FastAPI 文档中的示例: FastApi Oauth2

oauth2_scheme = OAuth2PasswordBearer(tokenUrl='login')

SECRET_KEY = settings.SECRET_KEY
ALGORITHM = settings.ALGORITHM
ACCESS_TOKEN_EXPIRE_MINUTES = settings.ACCESS_TOKEN_EXPIRE_MINUTES


def verify_access_token(token: str, credentials_exception):
  try:
    payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
    id: str = payload.get("user_id")
    if id is None:
      raise credentials_exception
    token_data = schemas.TokenData(id=id)
  
  except JWTError:
    raise credentials_exception

  return token_data

def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(database.get_db)):
  credentials_exception = HTTPException(
    status_code=status.HTTP_401_UNAUTHORIZED,
    detail=f"Could not validate credentials",
    headers={"WWW-Authenticate": "Bearer"}
  )

  token = verify_access_token(token, credentials_exception)
  user = db.query(models.User).filter(models.User.id == token.id).first()

  return user
Run Code Online (Sandbox Code Playgroud)

以下是受保护端点的示例,该端点依赖于上面列出的 oauth2 文件中的 get_current_user 函数。

@router.get("/", response_model=List[schemas.PostOut])
def get_posts(db: Session = Depends(get_db), current_user: int = Depends(oauth2.get_current_user):
  return {"Message": "Protected Endpoint Reached"}
Run Code Online (Sandbox Code Playgroud)

看来我遇到了这个问题,因为 Oauth2 中的 get_current_user 函数只能在以下格式时从标头中获取 JWT:

身份验证:不记名令牌

当令牌采用以下任一格式时,它似乎无法从标头中验证令牌:

Cookie:access_token =“承载令牌”

Cookie:身份验证=“承载令牌”

当我通过 HttpOnly Cookie 发送标头时,我是否需要以某种方式更改发送标头的方式,或者我是否需要更改有关 get_current_user 函数的某些内容,以使其能够正确读取 cookie 标头。

非常感谢任何建议,感谢您花时间阅读本文!

Mat*_*ndh 5

要从 cookie 而不是AuthorizationOAuth2PasswordBearer 默认的标头获取令牌,请告诉 FastAPI 您希望令牌源自 cookie

def get_current_user(access_token: str = Cookie(...), db: Session = Depends(database.get_db)):
Run Code Online (Sandbox Code Playgroud)

这假设令牌已被命名access_token(而不仅仅是令牌)。根据需要调整名称。