Search code examples
node.jsamazon-web-servicesaws-lambdagraphqlaws-appsync

NodeJS - Simplify/Resolve GraphQL query


I am currently writing a Lambda authorizer for an AWS AppSync API, however the authorization depends on the target resource being accessed. Every resource has their own ACL listing the users and conditions for allowing access to it.

Currently the best I could find would be to get the identity of the caller, look at all the ACLs, and authorize the call while denying access to all the other resources, what's not only highly inefficient, but also extremely impractical, if not impossible.

The solution I had originally came up with was to get the target resource, retrieve the ACL and check if the user fits the specified criteria. The problem is that I am unable to reliably define what's the target resource. What I get from AWS is a request like this:

{
    "authorizationToken": "ExampleAUTHtoken123123123",
    "requestContext": {
        "apiId": "aaaaaa123123123example123",
        "accountId": "111122223333",
        "requestId": "f4081827-1111-4444-5555-5cf4695f339f",
        "queryString": "mutation CreateEvent {...}\n\nquery MyQuery {...}\n",
        "operationName": "MyQuery",
        "variables": {}
    }
}

So, I only have the query string and variables, leaving the actual parsing of this to me. I got to convert it to an AST using graphql-js, but it's still extremely verbose and most importantly, it's structure varies greatly. My first code to retrieve the target worked for the AppSync console queries, but not the Amplify Front-End, for example. I also can't rely on something as simple as the variable name, as an attacker could quite easily craft a query with an arbitrary name, or even not use variables at all.

I thought about implementing this authorization logic within Lambda Resolvers, what should be simpler in a way, but would require me to use resolvers as authorizers, what doesn't seem ideal, and implement the entire resolver logic when I just want the most trivial possible resolvers.

Ideally I'd like something like this:

/* Schema:
type Query {
    operationName(key: KEY!): responseType
}*/

/* Query:
query abitraryQueryName($var1: KEY!) {
  operationName(key: $var1) {
    field1
    field2
  }
}*/

/* Variables:
{ "var1": "value1" } */

parsedQuery = {
    operation: "operationName",
    params: { "key": "value1" },
    fields: [ "field1", "field2" ]
};

Is there any way to resolve/simplify the queries from GraphQL to JSON/similar in a way that this information can be easily extracted?


Solution

  • Well, couldn't find anything on it, so I made something myself.

    On the off chance someone needs something similar, here's the gist with the code I used: https://gist.github.com/Iorpim/6544dad46060522dd0b17477871bc434

    I didn't make it a proper full lib, as it's a very specific use case and it's likely a one-off, and I am also not sure how reliable it is, but it solves my problem!