使用 FastAPI 在基于 Python 的 GraphQL 服务器中进行身份验证验证

jma*_*ndt 4 python authentication graphql starlette fastapi

我在使用 FastAPI 构建的 GraphQL 服务器中实现身份验证验证时遇到问题。之前,我们使用 REST,但现在我们正在切换到 GraphQL,我想知道如何实现这一点。之前,我们有不同的路由器,并与FastAPI很容易基于使用的依赖关系为途径,以检查认证在这里。我们在授权标头中发送一个令牌,我们在后端对其进行解码并取回 user_id,然后我们可以在不同的端点中使用它。

我想知道这里如何使用 GraphQL。我们使用石墨烯,我查看了Starlettes 身份验证示例以及设置GraphQl 的介绍

import binascii
from fastapi import FastAPI
from starlette.authentication import (
    AuthenticationBackend, AuthenticationError, SimpleUser, AuthCredentials
)
from starlette.graphql import GraphQLApp
from starlette.middleware import Middleware
from starlette.middleware.authentication import AuthenticationMiddleware

from schemas.root import my_schema


class BasicAuthBackend(AuthenticationBackend):
    async def authenticate(self, request):
        if "Authorization" not in request.headers:
            raise AuthenticationError('No auth credentials')

        auth = request.headers["Authorization"]
        try:
            id_token = auth.split('Bearer ')[1]
            decoded_token = auth.verify_id_token(id_token)

        except (ValueError, UnicodeDecodeError, binascii.Error) as exc:
            raise AuthenticationError('Invalid basic auth credentials')

        user_id = decoded_token['uid']
        return AuthCredentials(["authenticated"]), user_id


middleware = [
    Middleware(AuthenticationMiddleware, backend=BasicAuthBackend())
]

my_schema = Schema(
    query=RootQuery,
    mutation=RootMutation,
)

api = FastAPI(title=f"MyGraphQLServer", middleware=middleware)
api.add_route("/graphql", GraphQLApp(schema=my_schema))
Run Code Online (Sandbox Code Playgroud)

例如,假设我现在只想验证变异请求而不是查询请求。此外,我想访问每个解析器中的 user_id。什么是最好的方法来做到这一点?

Yag*_*nci 5

在他们使用的 FastAPI 文档或 starlette 文档中add_route,这是在 Starlette 中添加路由而不声明特定操作的方法(就像使用 .get()、.post() 等)。但它有一些缺点,我们不能像在 FastAPI 中那样添加依赖项,示例如下

app.add_route(
"/graphql",
GraphQLApp(schema=graphene.Schema(query=Query), 
executor_class=AsyncioExecutor),
    dependencies=(Depends(SomeAuthorizationStuffHere)),
)
Run Code Online (Sandbox Code Playgroud)

所以我们需要在 FastAPI 中做,我用 HTTPBasicAuth 创建了一个简单的应用程序,你可以用其他方法扩展它,你只需要包含 router(s)

from fastapi import Query, Depends, Request, FastAPI, APIRouter
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import graphene
from graphene import Field, Schema, String, ObjectType
from starlette.graphql import GraphQLApp


router = APIRouter()
app = FastAPI()
security = HTTPBasic()


class Query(ObjectType):
    hello = Field(String, name=String())

    def resolve_hello(root, info, name):
        coverage = info.context["request"].state.some_dep
        return f"Hello {some_dep.some_method(name)}"


graphql_app = GraphQLApp(schema=Schema(query=Query))


@router.api_route("/gql", methods=["GET", "POST"])
async def graphql(request: Request):
    return await graphql_app.handle_graphql(request=request)


app.include_router(router, dependencies=[Depends(security)])
Run Code Online (Sandbox Code Playgroud)