首页 > 解决方案 > Python Authlib:如何解决身份验证代码挑战并验证存储在受保护端点的仅 HTTP 会话 Cookie 中的令牌?

问题描述

阅读文档后,我很难理解如何使用Authlib为 OpenID Connect 提供程序实现授权代码流。阅读完文档后,我尝试实现下面列出的以下代码。

/login端点使用 authlib 重定向到身份提供者的授权,在本例中为 Cognito 。这重定向到/aws_cognito_redirect我目前自己实现的以解决检索令牌的代码挑战。

我的问题是:

更新

在检查了源代码后,我最终想出了如何使用Authlib和 FastAPI 来解决代码挑战。源代码包含在此问题的末尾。

由于第二部分仍未得到解答,因此我将问题悬而未决。目前,这个问题表明可以使用ResourceProtector可以满足我需要的类。但是,它有一个parse_request_authorization方法可以检查承载令牌请求的 Authorization 标头。所以...我假设该方法是子ResourceProtector类化并覆盖此方法以检查仅 HTTP cookie 的请求并提取其中包含的 JWT 以进行验证?此功能是否由Authlib实现和提供?

或者,也调查一下我是否可以集成fastapi-login来实现此功能。

附录:源代码

用于解决代码挑战的自定义实现的初始源代码

import base64
from functools import lru_cache

import httpx
from authlib.integrations.starlette_client import OAuth
from fastapi import Depends, FastAPI, Request, Response
from fastapi.responses import RedirectResponse
from starlette.middleware.sessions import SessionMiddleware

from . import config


@lru_cache()
def get_settings() -> config.Settings:
    """Create config settings instance encapsulating app config."""

    return config.Settings()


def get_auth_base_url(region: str, userpool_id: str) -> str:
    # base_url = "https://cognito-idp.us-east-2.amazonaws.com/us-east-2_QqNgzdtT5"

    base_url = "https://cognito-idp." + region + ".amazonaws.com/" + userpool_id

    return base_url


app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="secretly")

oauth = OAuth()
oauth.register(
    "cognito",
    client_id=get_settings().client_id,
    client_secret=get_settings().client_secret,
    server_metadata_url=get_auth_base_url(
        get_settings().region, get_settings().userpool_id
    )
    + "/.well-known/openid-configuration",
    client_kwargs={"scope": "openid email"},
)


def encode_auth_header(client_id: str, client_secret: str) -> str:
    """Encode client id and secret as base64 client_id:client_secret."""

    secret = base64.b64encode(
        bytes(client_id, "utf-8") + b":" + bytes(client_secret, "utf-8")
    )

    return "Basic " + secret.decode()


@app.get("/login")
async def login(request: Request):
    """Redirect to /aws_cognito_redirect endpoint."""

    cognito = oauth.create_client("cognito")
    redirect_uri = request.url_for("read_code_challenge")

    return await cognito.authorize_redirect(request, redirect_uri)


@app.get("/aws_cognito_redirect")
async def read_code_challenge(
    request: Request,
    response: Response,
    settings: config.Settings = Depends(get_settings),
):
    """Retrieve tokens from oauth2/token endpoint and return session cookie."""

    code = request.query_params["code"]
    print("/aws_cognito_redirect received code := ", code)

    auth_secret = encode_auth_header(settings.client_id, settings.client_secret)

    headers = {"Authorization": auth_secret}
    print("Authorization:" + str(headers["Authorization"]))

    payload = {
        "client_id": settings.client_id,
        "code": code,
        "grant_type": "authorization_code",
        "redirect_uri": settings.redirect_uri,
    }

    token_url = (
        "https://"
        + settings.domain
        + ".auth."
        + settings.region
        + ".amazoncognito.com/oauth2/token"
    )

    async with httpx.AsyncClient() as client:
        tokens = await client.post(
            token_url,
            data=payload,
            headers=headers,
        )
        tokens.raise_for_status()

        print("Tokens\n" + str(tokens.json()))
        response.set_cookie(key="jwt", value=tokens.content, httponly=True)

更新源代码以演示如何使用 Authlib 解决代码挑战

import base64
from functools import lru_cache

from authlib.integrations.starlette_client import OAuth
from fastapi import FastAPI, Request
from starlette.middleware.sessions import SessionMiddleware

from . import config


@lru_cache()
def get_settings() -> config.Settings:
    """Create config settings instance encapsulating app config."""

    return config.Settings()


@lru_cache
def get_auth_base_url(region: str, userpool_id: str) -> str:
    """Return cognito discover points base url from region and userpool ID."""

    return ("https://cognito-idp." + region + ".amazonaws.com/" + userpool_id)


app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="some-random-string")

oauth = OAuth()
cognito = oauth.register(
    "cognito",
    client_id=get_settings().client_id,
    client_secret=get_settings().client_secret,
    server_metadata_url=get_auth_base_url(
        get_settings().region, get_settings().userpool_id
    )
    + "/.well-known/openid-configuration",
    client_kwargs={"scope": "openid email"},
)


def encode_auth_header(client_id: str, client_secret: str) -> str:
    """Encode client id and secret as base64 client_id:client_secret."""

    secret = base64.b64encode(
        bytes(client_id, "utf-8") + b":" + bytes(client_secret, "utf-8")
    )

    return "Basic " + secret.decode()


@app.get("/")
async def login(request: Request):
    """Redirect to /aws_cognito_redirect endpoint after sign-in."""

    redirect_uri = request.url_for("read_code_challenge")

    return await cognito.authorize_redirect(request, redirect_uri)


@app.get("/aws_cognito_redirect")
async def read_code_challenge(request: Request):
    """Request a token from cognito using code challenge response."""

    return await cognito.authorize_access_token(request)

标签: pythonopenid-connectauthlib

解决方案


推荐阅读