AppSync IAM Policy Scoped for Schema Introspection and a Single Query
6/27/2020
✏️ Summary
Every API has its requirements. Some APIs are completely public, others might have strict access policies for certain resources. In the world of GraphQL, there is generally one endpoint. However, the queries and mutations available within that endpoint can be scoped, for example at the request level or maybe programmatically within the query or mutation for let’s say something like a user group.
When using AWS Appsync, there are quite a few ways to do this. It can be done using an API key, an IAM user with a policy, or even using services like Cognito.
In this post, won’t be going into a full example of how to set up a GraphQL using AWS Appsync. If you’re interested in learning more using a fully baked example, check out GraphQL with AWS AppSync and Go Lambdas.
🔎 Use Case
In our example, let’s pretend a user has another API or program that wants to use our API. However, we only want them to be able to access certain queries, mutations, and the GraphQL schema introspection. Then we will need an IAM user with a policy to provide programmatic access to our resources.
So first we need to create our GraphQL AWS Appsync. You are welcome to use the AWS Console in a web browser. The examples here will use CloudFormation.
💻 Example
Create API and schema
Let’s assume we have a schema.graphql in the root of project.
AppSyncAPI: Type: AWS::AppSync::GraphQLApi Properties: Name: "your_cool_graphql_api_name" AuthenticationType: AWS_IAM
AppSyncSchema: Type: AWS::AppSync::GraphQLSchema Properties: ApiId: !GetAtt [AppSyncAPI, ApiId] DefinitionS3Location: ./schema.graphqlSchema Definition
In our previous example, GraphQL with AWS AppSync and Go Lambdas, API keys were used for authentication. This is great for simple security, however when trying to be more granular, using an IAM user with a policy makes things a bit more flexible.
"""A coffee type for our coffee resource type"""type Coffee @aws_iam { id: ID! name: String! origin: String! roast: String!}
"""Create coffee input for our coffee resource type"""input CreateCoffee @aws_iam { name: String! origin: String! roast: String!}
"""Update coffee input for our coffee resource type"""input UpdateCoffee @aws_iam { id: ID! name: String origin: String roast: String}
"""Delete coffee input for our coffee resource type"""input DeleteCoffee @aws_iam { id: ID name: String origin: String roast: String}
"""Input for getting a random coffee"""input RandomCoffees @aws_iam { quantity: Int}
"""Connection type for our coffee resource to help support pagination"""type CoffeeConnection @aws_iam { items: [Coffee] nextToken: String}
"""A type for the result of requesting a random coffee"""type RandomCoffeeResult @aws_iam { coffees: [Coffee]}
"""The queries our service supports"""type Query @aws_iam { """ Get a coffee resource by ID """ getCoffee(id: ID!): Coffee """ Get all coffee resources """ allCoffees(nextToken: String): CoffeeConnection """ Get a random set of coffee resources """ getRandomCoffees(input: RandomCoffees): RandomCoffeeResult}
"""The mutations our service supports"""type Mutation @aws_iam { """ Create a coffee resource """ createCoffee(input: CreateCoffee!): Coffee """ Update a coffee resource """ updateCoffee(input: UpdateCoffee!): Coffee """ Delete a coffee resource """ deleteCoffee(input: DeleteCoffee!): Coffee}
"""Our schema"""schema { query: Query mutation: Mutation}IAM User and Policy
Then let’s make an IAM user with a policy that is scoped to just getting a single coffee by ID and permission to get the schema introspection. Access to the schema introspection is a big deal. It allows users to be able to get the schema documentation. One of the big benefits to using GraphQL is that it’s self documenting.
CoolIAMUser: Type: AWS::IAM::User Properties: UserName: "your_cool_graphql_iam_user" ManagedPolicyArns: - !Ref CoolIAMUserPolicy
CoolIAMUserPolicy: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: your_cool_graphql_iam_user_policy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - appsync:GraphQL Resource: - !Sub "${AppSyncAPI.Arn}/types/Query/fields/__schema" - !Sub "${AppSyncAPI.Arn}/types/Query/fields/getCoffee"✌️ Review
This was just a brief summary of how to use IAM to add security to an AWS Appsync GraphQL API endpoint. The inspiration for this post was actually being frustrated with how to add security for the introspection query. Even though the GraphQL documentation does exist for specifying __schema, it’s not listed as an example anywhere that I could find on Appsync documentation. So after some serious struggle, I threw up a proxy to see what the Insomnia Client was querying for. Then was able to reverse engineer it a bit. This was definitely the result of just being a GraphQL noob, but I figured it was worth sharing, so people could not lose their precious time. Furthermore, make sure to check out GraphQL with AWS AppSync and Go Lambdas if you want a full example of how all of this works together. Thanks for reading 🍻