Python 3.x 是否有一个FastAPI库可用于将端点标记为受保护并验证仅HTTP Cookie中的Auth JWT令牌?

Python 3.x 是否有一个FastAPI库可用于将端点标记为受保护并验证仅HTTP Cookie中的Auth JWT令牌?,python-3.x,amazon-cognito,openid-connect,openid,fastapi,Python 3.x,Amazon Cognito,Openid Connect,Openid,Fastapi,我试图学习和使用AWS Cognito用户池,并与Python FastAPI集成,并使用Python FastAPI实现API。到目前为止,我正在使用授权代码流和我的Cognito用户池重定向到FastAPI上的端点来解决代码挑战。源代码附加在此查询的末尾 API具有以下端点: 根端点[/]:将浏览器重定向到my AWS Cognito用户池的登录页面 重定向端点[/aws\u cognito\u Redirect]:成功登录到用户池后激活。从cognito用户池接收代码质询。在下面显示的代码

我试图学习和使用AWS Cognito用户池,并与Python FastAPI集成,并使用Python FastAPI实现API。到目前为止,我正在使用授权代码流和我的Cognito用户池重定向到FastAPI上的端点来解决代码挑战。源代码附加在此查询的末尾

API具有以下端点:

  • 根端点[/]:将浏览器重定向到my AWS Cognito用户池的登录页面
  • 重定向端点[/aws\u cognito\u Redirect]:成功登录到用户池后激活。从cognito用户池接收代码质询。在下面显示的代码中,
    aws\u cognito\u redirect
    端点通过向aws cognito用户池
    oauth2/token
    端点发送代码质询、重定向uri、客户端id等来解决代码质询。我可以在控制台日志输出中看到,标识、访问和刷新令牌已成功检索
  • FastAPI还将具有一些受保护的端点,这些端点将从web应用程序调用。此外,还将有一个web表单与端点进行交互

    在这个阶段,我可以使用FastAPI jinja2模板实现和托管webforms。如果我使用这个选项,我大概可以让
    /aws\u cognito\u redirect
    端点在仅HTTP会话cookie中返回令牌。这样,每个后续的客户端请求都会自动包含cookie,而不会在浏览器本地存储中公开任何令牌。我知道我必须使用此选项处理XSRF/CSRF

    或者,我可以使用Angular/React实现前端。据推测,建议的做法似乎是,我必须将授权流重新配置为PKCE的身份验证代码?在这种情况下,Angular/React web客户端将直接与AWS Cognito通信,以检索将转发到FastAPI端点的令牌。这些令牌将存储在浏览器的本地存储器中,然后在每个后续请求的授权标头中发送。我知道这种方法会受到XSS攻击

    考虑到我的需求,我认为我倾向于使用jinja2模板在FastAPI上托管webapp,并在成功登录时返回一个仅限HTTP的会话cookie

    如果我选择了这个实现路径,是否有一个FastAPI特性或Python库允许端点被修饰/标记为
    auth required
    ,以检查会话cookie的存在并执行令牌验证

    FastAPI

    导入base64
    从functools导入lru\U缓存
    进口httpx
    从fastapi导入依赖、fastapi、请求
    从fastapi.responses导入重定向响应
    从…起导入配置
    app=FastAPI()
    @lru_缓存()
    def get_设置():
    “”“创建封装应用程序配置的配置设置实例。”“”
    返回config.Settings()
    def encode_auth_头(客户端id:str,客户端机密:str):
    “”“将客户端id和密码编码为base64客户端id:client\u secret。”“”
    secret=base64.b64编码(
    字节(客户端id,“utf-8”)+b:“+字节(客户端机密,“utf-8”)
    )
    返回“Basic”+secret.decode()
    @app.get(“/”)
    def read_root(设置:config.settings=Depends(获取设置)):
    登录\u url=(
    “https://”
    +设置.domain
    +“.auth。”
    +设置。区域
    +“.amazoncignito.com/login?客户端id=”
    +settings.client\u id
    +“&response\u type=code&scope=email+openid&redirect\u uri=”
    +settings.redirect\u uri
    )
    打印(“重定向到”+登录地址)
    返回重定向响应(登录\ url)
    @app.get(“/aws\u cognito\u redirect”)
    异步def读取代码挑战(
    请求:请求,设置:配置。设置=依赖(获取设置)
    ):
    “”“从oauth2/令牌终结点检索令牌”“”
    代码=请求。查询参数[“代码”]
    打印(“/aws\u cognito\u重定向收到的代码:=”,代码)
    auth\u secret=encode\u auth\u头(settings.client\u id,settings.client\u secret)
    headers={“授权”:auth_secret}
    打印(“授权:+str(标题[“授权”]))
    有效载荷={
    “客户端id”:settings.client\u id,
    “代码”:代码,
    “授权类型”:“授权代码”,
    “redirect_uri”:settings.redirect_uri,
    }
    令牌\u url=(
    “https://”
    +设置.domain
    +“.auth。”
    +设置。区域
    +“.amazoncignito.com/oauth2/token”
    )
    使用httpx.AsyncClient()作为客户端进行异步:
    令牌=wait client.post(
    令牌地址,
    数据=有效载荷,
    标题=标题,
    )
    打印(“Tokens\n”+str(Tokens.json()))
    
    FastAPI高度依赖依赖依赖项注入,依赖项注入也可用于身份验证。您只需编写一个简单的依赖项来检查cookie:

    async def verify_access(secret_token: Optional[str] = Cookie(None)):
        if secret_token is None or secret_token not in valid_tokens:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid authentication credentials",
            )
        return secret_token
    
    并将其作为依赖项在视图中使用:

    @app.get("/")
    def read_root(settings: config.Settings = Depends(get_settings), auth_token = Depends(verify_access)):
        ...
    
    app = FastAPI()
    auth_required_router = APIRouter()
    
    app.include_router(
        auth_required_router, dependencies=[Depends(verify_access)],
    )
    
    @auth_required_router.get("/")
    def read_root(settings: config.Settings = Depends(get_settings)):
        ...
    
    如果要保护一组端点,可以定义附加路由器,该路由器将始终包含
    verify\u access
    作为依赖项:

    @app.get("/")
    def read_root(settings: config.Settings = Depends(get_settings), auth_token = Depends(verify_access)):
        ...
    
    app = FastAPI()
    auth_required_router = APIRouter()
    
    app.include_router(
        auth_required_router, dependencies=[Depends(verify_access)],
    )
    
    @auth_required_router.get("/")
    def read_root(settings: config.Settings = Depends(get_settings)):
        ...
    
    请注意,您的身份验证依赖项返回的值是任意的,因此您可以在那里返回在您的用例中有意义的任何内容(例如,经过身份验证的用户帐户)。如果要在由
    auth\u required\u路由器注册的视图中检索此值,只需在视图参数中定义此依赖项即可。FastAPI将只解析(并执行)此依赖项一次

    您甚至可以执行更复杂的操作,例如创建两个嵌套依赖项,一个简单地检查身份验证,另一个从数据库检索用户帐户:

    async def authenticate(...):
        ... # Verifies the auth data without fetching the user
    
    
    async def get_auth_user(auth = Depends(authenticate):
        ... # Gets the user from the database, based on the auth data
    
    现在,您的
    auth\u required\u路由器
    只能具有
    authenticate
    依赖项,但每个视图也需要访问