Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authentication verification in Python based GraphQL Server using FastAPI

I am having trouble implementing authentication verification in an GraphQL Server built with FastAPI. Before, we were using REST but now we are switching to GraphQL and I am wondering how I can implement this. Before, we had different routers and with FastAPI it is easy to check authentication based on routes using dependencies as in here. We are sending a Token in the Authorization Header which we are decoding in the backend and getting back the user_id which we can then use in our different endpoints.

I am wondering how this might work using GraphQL here. We use the Graphene and I had a look at Starlettes Authentication Examples as well as there intro into setting up 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))

For example, imagine that I now would only like to authenticate mutation Requests but not query requests. Furthermore I want to access the user_id in each of my resolvers. What would be the best way to do this?

like image 755
jmandt Avatar asked Aug 03 '20 14:08

jmandt


1 Answers

In FastAPI documentation or starlette documentation they use add_route, that is the way to add a route in Starlette without declaring the specific operation (as would be with .get(), .post(), etc). But it has some disadvantages, we can't add dependencies like we do in FastAPI, example below

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

So we need to do in FastAPI, i created a simple app with HTTPBasicAuth, you can expand this with other Method's you just need to include the 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)])
like image 176
Yagiz Degirmenci Avatar answered Sep 25 '22 13:09

Yagiz Degirmenci