AppSync IAM Policy Scoped for Schema Introspection and a Single Query

6/27/2020

AppSync IAM Policy Scoped for Schema Introspection and a Single Query

✏️ 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.graphql

Schema 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 🍻

Back to all posts