Search code examples
graphqlapollo-federationapollo-gatewaygraphql-federation

Centralized Authorization in Graphql Federation


I'm looking for an approach to centralize authorization in my Graphql setup. here is the full context of the problem I'm facing

i have one supergraph, and two subgraphs, let's call them posts, and users.

posts are public to everyone, while users are private to admins, or people with rights to see the users. another subgraph is going to be implemented, let's call them products, data in this subgraph will also be private to some users. which means now I need to implement the authorization in my new subgraph. so instead i was wondering if I could take the authorization out of my user, move it to my supergraph, and then just reuse it in my sell subgraph.

what I have tried now is a directive to use on object types, this directive works fine in each subgraph, while when I move it to my supergraph it doesn't work anymore. here is a link to the project in github: https://github.com/rezaerami/gql-federation

so long story short, I want to make my subgraphs authorization agnostic, and just put the behind IP protection, and my subgraph enforce the permission on resolvers.


Solution

  • well since with directive it was impossible to do what I wanted to do, I decided to use willSendRequest hook using a function that checks the permissions of queries against user permissions I managed to do it

    here is my check permission code

    const {parse} = require("graphql/language");
    
    const permissions = {
        query: {
            product: ["admin", "user"],
            seller: ["admin"],
        }
    }
    
    const checkPermission = (req, context) => {
        const document = parse(req.query);
        const unauthorizedOperations = [];
    
        document.definitions.forEach(definition => {
            definition.selectionSet.selections.forEach(selection => {
                if (permissions?.[definition.operation]?.[selection.name.value]) {
                    if (!permissions[definition.operation][selection.name.value].some(permission => context.user.role.includes(permission))) {
                        unauthorizedOperations.push({
                            operation: definition.operation,
                            selection: selection.name.value,
                        })
                    }
                }
            })
        })
        if (unauthorizedOperations.length) {
            throw new Error(`Unauthorized permissions for ${unauthorizedOperations[0].operation}.${unauthorizedOperations[0].selection}`);
        }
    }
    
    module.exports = checkPermission
    

    then I added this line on willSendRequest

    checkPermission(request, context)
    

    it is not ideal since it's a trick basically and doesn't use built-in features of graphql, but it does what I wanted to do!