Appsync IAM policy scoped for schema introspection and a single query
✏️ Summary
Every API has it’s own 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 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 the a user group.
When using AWS Appsync there quite a few ways to do this. It can be done using an API key, 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 setup 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. Feel free 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. Also 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 🍻
📨 Questions, Issues, and Suggestions?
Please feel free to send over any questions, issues, or suggestions.