Tengo problemas para implementar la verificación de autenticación en un servidor GraphQL creado con FastAPI. Antes, usábamos REST, pero ahora estamos cambiando a GraphQL y me pregunto cómo puedo implementar esto. Antes, teníamos diferentes enrutadores y con FastAPI es fácil verificar la autenticación basada en rutas usando dependencias como aquí . Estamos enviando un token en el encabezado de autorización que estamos decodificando en el backend y recuperando el user_id que luego podemos usar en nuestros diferentes puntos finales.
Me pregunto cómo podría funcionar esto usando GraphQL aquí. Usamos Graphene y eché un vistazo a los ejemplos de autenticación de Starlettes , así como a la introducción a la configuración de 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))Por ejemplo, imagine que ahora solo me gustaría autenticar solicitudes de mutación pero no solicitudes de consulta. Además, quiero acceder a user_id en cada uno de mis resolutores. Cuál sería la mejor forma de hacer esto?
En la documentación de FastAPI o documentación de starlette usan add_route , esa es la forma de agregar una ruta en Starlette sin declarar la operación específica (como sería con .get(), .post(), etc). Pero tiene algunas desventajas, no podemos agregar dependencias como lo hacemos en FastAPI, ejemplo a continuación
app.add_route( "/graphql", GraphQLApp(schema=graphene.Schema(query=Query), executor_class=AsyncioExecutor), dependencies=(Depends(SomeAuthorizationStuffHere)), ) Entonces, debemos hacerlo en FastAPI, creé una aplicación simple con HTTPBasicAuth, puede expandir esto con otros métodos, solo necesita incluir el 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)])