How to implement user authorization & fine grained access control in a app using AWS AppSync with Amazon Cognito & AWS Amplify. GraphQL If this is your first time using AWS AppSync, I would probably recommend that you check out tutorial before following along here. this If you are already familiar with AWS AppSync & want to dive deeper on more complex user authorization examples, check out recent post by . this Richard Threlkeld In this post, we’ll look at how to only allow authorized users to access data in a GraphQL API. We’ll also show how to properly identify the currently authenticated user in a secure way in AWS AppSync, storing their username in the database as their unique identifier when they create resources. Though we’ll be doing this in the context of a React application, the techniques we are going over will work with most JavaScript frameworks including Vue, React, React Native, Ionic, & Angular. The flow that we will be working with looks like this: As a user, we log in to the application and receive an identity token. We invoke a GraphQL query or mutation from the client application, passing the user identity token along with the request in an authorization header (the identity automatically passed along by the AWS AppSync client). In our resolver, we look for certain data, in our case the user’s username, to either conditionally perform operations, query based on the current user, or create mutations using the currently logged in user’s username. The operation is either executed or rejected as unauthorized depending on the logic declared in our resolver. The data flow for a mutation could look something like this: User executes a GraphQL operation sending over their data as a mutation. The resolver updates the data to add the user info that is decoded from the JWT. The JWT is sent in the authorization header & is available in the resolver. Data is stored in the database along with user information. In this example we can now query based on the author index. Getting Started The tools that we will be using to accomplish this are the AWS Amplify CLI to create the authentication service & the AWS Amplify JavaScript Client for client authentication as well as for the GraphQL client. If you are not already familiar with how to use AWS Amplify with Cognito to authenticate a user and would like to learn more, check out either or . React Authentication in Depth React Native Authentication in Depth To get started, clone the boilerplate we will be using in this example: git clone https://github.com/dabit3/appsync-react-native-with-user-authorization Then, cd into the directory & install the dependencies using yarn or npm: cd appsync-react-native-with-user-authorization yarn || npm i Now that the dependencies are installed, we will use the to initialize a new project. AWS Amplify CLI First, install the AWS Amplify CLI if you do not already have it installed: npm i -g @aws-amplify/cli Next, configure the cli with your correct credentials: If this is your first time using AWS, check out video to see how to get these credentials and set up the CLI. this amplify configure Now we can create a new project: amplify init You’ll be prompted with a few configuration options, feel free to accept the defaults to all of them or choose a custom project name when given the option. Next we will add user-signin capabilities to the app with Amazon Cognito: amplify add auth Then push the updated config to the AWS console amplify push Now, you should be able to visit the console and view the new service. Go to and click on the name of your project to see your current configuration. https://console.aws.amazon.com/cognito/users/ Creating the AWS AppSync API Now that our Amplify project is created and ready to go, let’s create our AWS AppSync API. First, go to the AWS AppSync console by visiting and clicking on Create API, then choose & give the API a name. https://console.aws.amazon.com/appsync/home Build from scratch Creating the Schema Now that the API has been created, click Settings and update the to be . In the User Pool configuration, choose the user pool that was created when we created our AWS Amplify project using the CLI along with your region, and set the default action to . Authorization type Amazon Cognito User Pool Allow Next, create the following schema and click : Save type City {id: ID!name: String!country: String!author: String} type Query {fetchCity(id: ID): City} Note that is the only field not required. author Provisioning Resources Next, click the Create Resources button. In this screen, choose as the type, and with an of and a primary key of . Then scroll to the bottom and click . City create an additional index Index name author-index author Create Updating the resolvers Next, we’ll update a couple of resolvers. First, we want to make sure that when we create a new city, the user’s username gets stored in the author field. This username data is available as part of the user identity token passed along with the request in an authorization header, and we can access this in our resolver as the in the field available in the resolver. identity context.identity Identifying the currently logged in user in a mutation In the resolver field under in the dashboard click on the resolver for : Mutation Data Types createCity Update the to the following: createCity request mapping template #set($attribs = $util.dynamodb.toMapValues($ctx.args.input))#set($attribs.author = $util.dynamodb.toDynamoDB($ctx.identity.username)){"version": "2017-02-28","operation": "PutItem","key": {"id": $util.dynamodb.toDynamoDBJson($util.autoId()),},"attributeValues": $util.toJson($attribs),"condition": {"expression": "attribute_not_exists(#id)","expressionNames": {"#id": "id",},},} There are a couple of things to note: In the first line of code we are creating a new map / object called and adding the existing values coming in as $attribs $ctx.args.input In the second line of code we are adding another field to the object called author with the value of . is the identity information coming via the user identity token. $ctx.identity.username $ctx.identity In the field, we are passing in the new object we just created. attributeValues $attribs Now, when we create a new city, the user’s identity will automatically be stored as another field in the DynamoDB table. Allowing read access to only the item author Now that we have a way to identify the user in a mutation, let’s make it to where when a user requests the data, the only fields they can access are their own. To add this functionality using our existing setup, we only need to do one thing: update the resolver to query only for the data created by the currently logged in user. listCities DynamoDB allows you to perform operations directly on an index. We will utilize this by querying the data from the table using the and again using the to identify the user. Query author-index $context.identity.username Update the to the following: listCities request mapping template {"version" : "2017-02-28","operation" : "Query","index" : "author-index","query" : {"expression": "author = :author","expressionValues" : {":author" : {"S": "${ctx.identity.username}"}}},"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.after, null)),} A couple of things changed here: We updated the operation to be a vs a . The difference is that a operation scans the entire table whereas a operation searches only primary key attribute values and supports a subset of comparison operators on key attribute values (in our case author) to refine the search process. Query Scan scan query We added an field specifying the index in which we would like to query index We added another new field, , defining the value we would like to use as the query value (in our case, author). query Now, the API is complete and we can begin testing it out. Testing the app Next, we’ll download the AWS AppSync configuration from our AWS AppSync Dashboard under the Integrate with your app section in the getting started screen, saving it as AppSync.js in our root folder. You should be able to run the app by running or . react-native run-ios react-native run-android From the opening screen, choose “Sign Up” and create a new user. Confirm the new user with 2 factor authentication (Make sure to add +1 or your country code when you input your phone number). Once you’ve signed up, sign in, click on , and create a new city: Add City Once you create a city, you should be able to click on the tab to view this new city. Cities Now, let’s go back into the AWS AppSync dashboard. Click on , and the table name. This will take you to DynamoDB. Data Sources In the items tab, you should now be able to see the fields along with the new Author field. If you manually add a new entry to the database with another author name, or you update an existing field changing the author name to one that is not your own & refresh your app, these cities with the updated fields should show up in your app as the resolver will return only the fields that you have written! not Conclusion When building a real world app there are many important and complex things that need to be taken into consideration, one of the most important being a real world scalable & easy to implement user authorization story. When using GraphQL, you also must need to take into consideration around not only scalability but also . best practices security GraphQL gives you the power to enforce different authorization controls for use cases like: A public API Private and Public access to sections of an API Private and Public records, checked at runtime on fields One or more users can write/read to a record(s) One or more groups can write/read to a record(s) Everyone can read but only record creators can edit or delete One of the most compelling things about AWS AppSync is its powerful user authorization features that allow all of these GraphQL user authorization use cases to be handled out of the box. built-in My Name is . I am a Developer Advocate at working with projects like and , and the founder of . Nader Dabit AWS Mobile AWS AppSync AWS Amplify React Native Training If you enjoyed this article, and share it! Thanks for your time. please clap n number of times Images courtesy of Amazon Web Services, Inc