Authenticating an AWS AppSync GraphQL API with Auth0

In this tutorial, we’ll walk through how to implement OIDC authentication for your AWS AppSync endpoint using Auth0 as the authentication provider.


AWS AppSync has multiple ways to authorize users in order to do things like general user authorization & fine grained access control. The service supports a built-in OIDC provider in Amazon Cognito User Pools as well as allowing you to bring your own auth provider using any OIDC spec complaint provider.

OpenID Connect is a popular standard for single sign-on & identity provision that uses JSON-based identity tokens delivered via OAuth 2.0 flows to handle identity management. If you’d like to learn more about OIDC, this is a really great write-up you can check out.

In this tutorial, we’ll look at how to use Auth0 as the OIDC authentication provider for your AWS AppSync API.

To view the corresponding Github repo for this project, click here.

Creating Auth0 API

The first thing we need to do is create a new Auth0 API in our Auth0 account. To do so, go into the Auth0 dashboard, click on APIs, & click the Create API button.

Next, give the API a name & identifier, leaving the signing algorithm to be RS256.

Now, our API has been created.

Next, we need to change the signing algorithm for OAuth to be RS256 as well (by default it is HS256). To do so:

  1. Click on Applications in the left hand menu.
  2. Choose the application you just created.
  3. Scroll to the bottom of the Settings tab & click Show Advanced Settings.
  4. In the OAuth tab, change the signing algorithm to RS256.

Now, our Auth0 application is ready to go, next we’ll create a new AWS AppSync API and configure it to work with our new Auth0 API.

Creating AWS AppSync API

Next, let’s go to the AWS AppSync console & create the new API.

From the main console view, click Create API.

In this view, choose Author From Scratch & give the API a name. I’m giving mine the name of AppSyncAuth0.

Once the API has been created, let’s create the resources that we will be working with in the API.

Click on Schema in the left menu, then click the Create Resources button in the top right corner:

From the following screen, we’ll leave Define New Type checked, & we’ll define the following type in our Schema:

type Dog {
id: ID!
name: String!
}

Scroll down to the bottom of the above screen & click Create.

Now that the resources have been created, click on Settings in the left menu.

Here, under Authorization type, we will change this to OpenID Connect.

For the OpenID Connect provider domain (Issuer URL) setting, we’ll pass in the Domain of our Auth0 application prepended with https. So your url should look something like https://yourdomain.auth0.com:

We can now immediately begin testing this API out once we receive an Access Token from Auth0.

There are a couple of ways to do fetch an Access Token:

  1. We can authenticate a user by signing them up & then signing them in, then grab the token from the url.

2. We can fetch one from our terminal by using a curl request with the following command:

curl --request POST \   --url https://yourdomain.auth0.com/oauth/token \   --header 'content-type: application/json' \   --data '{"client_id":"yourclientid","client_secret":"yourclientsecret","audience":"youridentifier","grant_type":"client_credentials"}'

3. We can go into the Auth0 dashboard, click on APIS, click on the API you would like to get an Access Token for, click on Test, the click on Copy Token in the example response:

Choice # 3 seems like the easiest way to go for now, so I’ll choose to copy it from the Auth0 console.

Testing the AWS AppSync API

Now, we can begin testing the API using the generated token.

In the AWS AppSync dashboard, click on Queries in the left hand menu.

In the query editor, we’ll create the following mutation:

mutation create {
createDog(input:{
name: "Frankie"
}) {
id
}
}

Next, we’ll paste in the Access Token that we received from our Auth0 dashboard:

Using this token, we should now be able to execute the mutation.

Connecting with a Client Application

Now, our API is ready to go & we can connect to it from a client application.

To hit the API, we need to set the authorization type as OIDC when we send requests from a client application.

We also need to be able to access the id_token. When logging in using Auth0, the id_token is provided in a callback in the URL. You can parse the URL & save the id_token to localstorage for usage by the AWS AppSync client.

Here’s how that might look wired up with the AWS AppSync JavaScript SDK:

const client = new AWSAppSyncClient({
url: AppSyncConfig.graphqlEndpoint,
region: AppSyncConfig.region,
auth: {
type: AppSyncConfig.authenticationType,
jwtToken: () => window.localStorage.getItem('AppSyncOIDCKey')
}
})
To view a very basic demo application that implements login in a React application & fetches data using AWS AppSync & Auth0, click here.

Once your client is configured to use the Auth0 JWT & we begin making calls to the AWS AppSync endpoint the application should not only authorize the request, but we should also be able to access the identity of the logged in user.

To demonstrate this, let’s update the request mapping template in the resolver for the createDog mutation.

In the AWS AppSync dashboard, click on Schema. On the right hand side of the screen, under Resolvers, find the createDog mutation under the Mutations heading & click on DogTable to change the resolver.

Update the request mapping template to the following:

#set($input = $ctx.args.input)
$util.qr($input.put("userId", $ctx.identity.sub))
{
"version": "2017-02-28",
"operation": "PutItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($util.autoId()),
},
"attributeValues": $util.dynamodb.toMapValuesJson($input),
"condition": {
"expression": "attribute_not_exists(#id)",
"expressionNames": {
"#id": "id",
},
},
}

This new request mapping template will now add a new property to all mutations defining the user of this mutation using their unique identifier, $ctx.identity.sub.

We can access the entire user identity in the request mapping template in $ctx.identity.

We store this property in the database in a column called userId.

We can now implement fine grained access control. Let’s query only for data stored by this user. This is fairly simple to do, we need to do only two things:

  1. Add a secondary index on our table defining the userId as the primary key for the index.
  2. Update the request mapping template for the listDogs query.

Adding a secondary index

To add a secondary index, click on DataSources in the AWS AppSync console & then click on the name of the table (DogTable).

In the DynamoDB table view, click on Indexes. Click Create Index & give the index a Partition Key of userId. (Feel free to drop the Read & Write Capacity Units down to 1 if you would like for our purposes of this tutorial but this is up to you).

Next, click Create Index.

Updating the Request Mapping Template

Now we’ll update the request mapping template for the listDogs query to only query for dogs created by the logged in user.

We can identify the user, again like we did in the createDog request mapping template, by accessing the identity object of the request by using the $ctx.identity, & specifically $ctx.identity.sub.

In the AWS AppSync dashboard, click on the resolver for the listDogs query. Update the request mapping template to the following:

{
"version" : "2017-02-28",
"operation" : "Query",
"index" : "userId-index",
"query" : {
"expression": "userId = :userId",
"expressionValues" : {
":userId" : {
"S": "${ctx.identity.sub}"
}
}
},
"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.after, null)),
}

Now, when querying for the list, we will only return the items that belong to that user.

Wrapping up

Much of the actual logic when dealing with identity around fine-grained access control will be done in the request & response mapping templates. These mapping templates are written in a templating language called VTL (Velocity Templating Language) that gives you full control over logic & your data.

If this is your first time working with AWS AppSync & you would like to learn more about VTL as well as working with the mapping templates, check out the documentation here.

To learn more about security with AWS AppSync, click here.