Authenticating a GraphQL API with AWS

<br>

Table of Contents

  • Adding the Authentication Provider with AWS Amplify
  • Updating AWS AppSync to use the new Authentication provider
  • Adding Fine-grained Access Control
  • 1.Updating the request mapping teamplate for createRestaurant
  • 2. Adding the userId Global Secondary Index to the DynamoDB Table
  • 3. Querying for data based on identity
  • Conclusion 


Right now, the configuration for the API is set to API Key. This means that any client that has the correct API Key sent as a header with the http request will be able to access the API to perform operations against it.


AWS AppSync Authentication Types

We would like to update the API so that only authenticated users can access it. We'd also like to add fine-grained access conrol so we can control access to certain data based on the signed in user. With AWS AppSync, we can either use Amazon Cognito User Pools or OpenID Connect to accomplish this.

OpenID Connect (OIDC) Authentication type enforces OIDC tokens provided by an OIDC-compliant service. Your application can leverage users and privileges defined by your OIDC provider for controlling access. This allows you to bring your own or use an existing identity provider to authenticate against your AppSync API.

Amazon Cognito User Pools Authentication type enforces OIDC tokens provided by the user pool being used. Your application can leverage the users and groups in your user pools and associate these with GraphQL fields for controlling access.


Adding the Authentication Provider with AWS Amplify


With the AWS Amplify CLI we can create a new authentication service & also update the authentication configuration of the AWS AppSync API we have already created.


To do so, we'll open the project where we left off in the previous tutorial & run the following command to create the authentication service:

amplify add auth
  • Do you want to use the default authentication and security configuration? Y
Choosing the default authentication & security configuration as we have done above will automatically set up some basica configuration around the authentication service, including email MFA for signing up, & requiring username & password for signing in.

Next, we'll run the following command to create the backend AWS resources in our account:

amplify push

Now, the authentication service is in our account & we can begin logging in!

To try this out, let's use the AWS Amplify React library to our project. This library contains multiple React specific functionality & UI components that we can use to quickly get up & running with useful features:

npm install aws-amplify-react

Now, we can use a React Higher Order Component (HOC) that is made available through the library to add an authentication flow in just a few lines of code:

// import HOC import { withAuthenticator } from 'aws-amplify-react' // this stays the same class App extends Component {} // update default export export default withAuthenticator(App, { includeGreetings: true })

If we run the app, we should see a sign-in / sign-up form now protecting the application:

If we sign up & then sign In, we should then be able to view the main application.


Updating AWS AppSync to use the new Authentication provider


Now that we have authentication set up we need to update the authentication type of our AWS AppSync API. Right now it's set to API Key, we need to update it to use the new Amazon Cognito User Pool configuration we just created.

This is easy to do, we only need to run one command to update this configuration:

amplify configure api

You'll be prompted for the floowing:

  • Please select from one of the below mentioned services: GraphQL
  • Choose an authorization type for the API: Amazon Cognito User Pool

Once this is completed, we'll run amplify push to update the resources in our account:

amplify push

Now, our API will only be available to signed in users & not accessible via regular API calls from unauthenticated users.

To test mutations & queries from the AppSync console query editor you will need to specify an app client & log in using that app client. To create an app client, go to the cognito user pool dashboard in the correct region, click on your user pool, & then click on App clients in the left hand menu to create a new app client. Make sure that Generate client secret is unchecked when creating the new App client.

Adding Fine-grained Access Control


Now that we have an authenticated GraphQL API, we may want to start implementing some authorization controls on the API. Typically, you'll have resources that you only want made available to certain users or certain groups.


Let's update our existing API to only allow the listRestaurants query to return the data of the logged in user.

To get this working, we need to do three things:

  1. In the request mapping template for the createRestaurant mutation, we will update the data being passed in to the mutation to include information about the user creating the mutation. We'll add another field in the table (userId) & populate the field with the user id of the user that is triggering the mutation. We'll accomplish this by updating the input field with an additional value that we'll get from the user's identity using the $context.identity value available in the request mapping template.
  2. We'll create a userId global secondary index in our table so that we can query only based on that index. This means that we can request data only if the index value matches the index that we are querying for. (To learn more, click here).
  3. Now we need a way to query for the data based on the user. In the response mapping mapping template, we'll use the secondary index to query only for the data that the user making the request created. We'll read the user's unique id in the request mapping template & query based on the value. We'll get the user's identity by accessing the $context.identity available in the mapping template.

1.Updating the request mapping teamplate for createRestaurant

We'll first update the request mapping template for the createRestaurant mutation. To access this mapping template, go to the AWS AppSync console & open your API. In the left hand menu, click on Schema.


While in the Schema view, you will see the resolvers listed on the right hand side. Scroll (or filter) to mutations & click the resolver for the createRestaurant mutation.

In this view, you'll see both the request mapping templae & the response mapping template for this mutation. We'll update the request mapping template to add a new userId field to the input field (context.args.input). To do so, we can add this line & save the :

$util.qr($context.args.input.put("userId", $context.identity.sub))


2. Adding the userId Global Secondary Index to the DynamoDB Table


Now when we create a new item, the user id of the user will also be stored along with the other item information.

We now would like to query the table based on this new userId field. To do so, we'll create a secondary index on the table so that we can query this index based on the user id.

To add a secondary index, we need to open the DynamoDB table for the API. Click on Data Sources in the left menu & then click on the link for the DynamoDB table. Next, click on the Indexes tab & click Create index. Set the Partition key as userId & the read / write capacity units to 1 & click *Create index *(it may take up to 5 minutes for index to be created).


3. Querying for data based on identity


Now that the secondary index is created & the user's identity is being stored in the database, we can query based on the user's identity. To do so, we'll update the request mapping template for the listRestaurants query resolver.


To update the resolver, click on Schema in the left menu to view the Schema & resolvers. In the resolvers list, scroll to (or filter for) query & click on the listRestaurants resolver. Update the request mapping template of the resolver to the following & click Save Resolver:

#set( $limit = $util.defaultIfNull($context.args.limit, 10) )
{ 
"version" : "2017-02-28", 
"operation" : "Query", 
"limit": $limit, 
"index": "userId-index", 
"query" : { 
  "expression": "userId = :userId", 
  "expressionValues" : { 
    ":userId" : $util.dynamodb.toDynamoDBJson($ctx.identity.sub) 
} 

},
"nextToken": #if( $context.args.nextToken )
"$context.args.nextToken"
#else
null
#end
}


In the updated request mapping template we are performing a query based on an index instead of a scan. We're querying the userId index & getting the user's unique id from the context.identity.sub value which is automatically made available from AppSync.

With this updated mapping template, when we call the listRestaurants query we'll only get the data for the user that is currently signed in.


Conclusion


While we've just scratched the surface of how you can implement authorization in an AWS AppSync GraphQL API, we've covered some very important fundamental concepts that can be expanded upon to handle many authorization & fine-grained access control use-cases.

If you'd like to learn more, take a deep-dive into the AWS AppSync Authorization Use Cases documentation here.


Building Robust GraphQL APIs with AWS Amplify and AWS AppSync

Building Robust GraphQL APIs with AWS Amplify and AWS AppSync

GraphQL is a solid way of building responsive web and mobile APIs. It's not always obvious, however, how to structure your APIs. When should you use a transformer? When are GraphQL subscriptions appropriate? In this tech talk, we'll go through the principles of building robust APIs powered by GraphQL for a range of interesting use cases.

GraphQL is a solid way of building responsive web and mobile APIs. It's not always obvious, however, how to structure your APIs. When should you use a transformer? When are GraphQL subscriptions appropriate? In this tech talk, we'll go through the principles of building robust APIs powered by GraphQL for a range of interesting use cases.

Learning Objectives:

  • Learn about the main principles that define robust GraphQL APIs
  • Learn about the building blocks that are available and when to use them
  • See how AWS Amplify and AWS AppSync make it easy to run your GraphQL backend

GraphQL API with AWS and Use with React

GraphQL API with AWS and Use with React

Create a serverless GraphQL API with AWS and consume it with React.

GraphQL has become a go-to API implementation for developers looking to take advantage of features liketype safety, network efficiency, real-time data with subscriptions & more.

Building, managing, & deploying your own GraphQL server can be tedious & time consuming, especially for developers new to the technology that may want to quickly get a real-world, secure & scalable GraphQL API up and running without having to learn how to create it from scratch (as well as spend the time learning best practices around API design & authorization).

Introducing AWS AppSync

Table of Contents

In this post we’ll learn how to use AWS AppSync, a fully managed GraphQL service, to create a serverless GraphQL API. AWS AppSync allows developers to quickly create serverless, scalable & secure GraphQL APIs.

We’ll go through:

Our CLI Tool: AWS Amplify

We’ll be using AWS Amplify to create the AppSync service & connect to it from our React application. AWS Amplify is a CLI & Client toolchain that allows developers to create AWS services from their front-end environment & connect to them from the client-side applications.

The app that we will be building is a restaurant tracker app that will allow us to keep up with all of the restaurants we would like to visit as well as store information about each restaurant. We’ll use React as the front-end for the application.

Getting Started

The AWS Amplify CLI is the tool that we will be using to create the AWS AppSync API. This CLI allows us to create & deploy AWS services directly from the command line. The services that we will be creating are AWS AppSync for the API layer & Amazon Cognito for the authentication provider.

App Features

Users will be able to sign up for an account, sign in to the account, create a restaurant, & query for only the restaurants that the user created.

Creating the React App

First, we’ll create the React application that we’ll be using to connect to our API. To do so, we’ll use Create React App:

create-react-app restaurant-app

# or
npx create-react-app restaurant-app

Once the app is created, we’ll change into the new directory & install aws-amplify:

cd restaurant-app
npm install aws-amplify

Amplify CLI

The next thing we need to do is install & configure the AWS Amplify CLI. To do so, we’ll use npm:

npm install -g @aws-amplify/cli

Once the CLI is installed, we’ll need to configure it to use an IAM user from our AWS account

amplify configure

If you’d like a video walkthrough of how to install & configure the AWS Amplify CLI, click here. Now that the CLI is installed & configured, we can initialize a new Amplify project in our React application. From the inside your React application, run the following command:

amplify init

If you’d like a video walkthrough of how to install & configure the AWS Amplify CLI, click here. Now that the CLI is installed & configured, we can initialize a new Amplify project in our React application. From the inside your React application, run the following command:
Once the project has been initialized, we can go ahead and add an an API & authentication to our app. To do so, we’ll first add authentication.

Adding the AWS AppSync API

To add the AWS AppSync api, we can run the following command:

amplify add api

Now, you’ll be prompted for the following:

Update the schema to this & save the file:

type Restaurant @model {
  id: ID!
  name: String!
  description: String
}

Next, we’ll run the push command to create the services in our account:

amplify push

Now, you’ll be prompted for the following:

Once the push is complete, the AWS AppSync service have been successfully created in our account.

What exactly has been created? If you notice, in the above schema definition, we have an @model directive on the schema type definition. This is part of the Amplify GraphQL Transform library.

The library will recognize this directive & automcatically expand the schema into additional schema that is typically necessary for a robust API, including queries, mutations, subscriptions, resolvers & a data source (Amazon DynamoDB). Everything you need to get up & running is now set up for you using this directive.

If at any time you would like to view the services that have been created in your Amplify configuration, you can run amplify status.

To view the AWS AppSync console & see the API that was just created, click here. To view the Cognito user pool that was just created, click here.

If you’d like a video walkthrough of how to install & configure the AWS Amplify CLI, click here. Now that the CLI is installed & configured, we can initialize a new Amplify project in our React application. From the inside your React application, run the following command:## Testing the API in the AWS Console

Now that the API has been created, we can go to the AWS AppSync console & begin testing it out! To do so, visit the AWS AppSync console (be sure you are in the correct region), & click on the API name you just created.

In the left hand menu, you will see links for your data source, settings, schema, & queries. Feel free to click around here & explore.

What we’d like to do next though is execute mutations & queries against the API. In the left hand menu, click on Queries & try out the following mutation in the query editor:

mutation create {
  createRestaurant(input: {
    name: "Soi"
    description: "Great Thai in Seattle"
  }) {
    id name d
  }
}

Creating a query in the AWS Console

Create a couple of mutations & then we’ll query for them using the following query:

query list {
  listRestaurants {
    items {
      id
      name
      description
    }
  }
}

This query should return an array of restaurans showing the id, name & description for each item created.

You may be wondering where the ID comes from, since all we did was pass in a name & description. The ids are automatically generated by the service (in the resolver mapping template) so you don’t have to pass them in. If you’d like to view the mapping template for any resolver, click on Schema in the left menu, look for the resolver you’d like to view in the resolvers panel on the right.

Integration with React

Now that the services are created, we can wire them up with our React application & begin interacting with them.

The first thing we’ll do is configure the application with our Amplify resources. To configure the React application, open index.js & add the following three lines of code:

import Amplify from 'aws-amplify'
import config from './aws-exports'
Amplify.configure(config)

Next, open App.js. Here, we’ll query for the restaurants & display them in our app. To do so, we’ll need to do 5 things:

  1. Import the APIs from AWS Amplify
  2. Import the query definition
  3. Create initial state to hold the restaurants array
  4. Query for the restaurants array from our API & update the state
  5. Display the restaurants array in the React application.

First, we’ll take care of our imports:

import { API, graphqlOperation } from 'aws-amplify'
import { listRestaurants } from './graphql/queries'

Next, in the class definition, we’ll go ahead & create the state & initialize the query in componentDidMount lifecycle method:

state = { restaurants: [] }
async componentDidMount() {
  try {
    const apiData = await API.graphql(graphqlOperation(listRestaurants))
    const restaurants = apiData.data.listRestaurants.items
    this.setState({ restaurants })
  } catch (err) {
    console.log('error: ', err)
  }
}

In componentDidMount we fetch the data from the AppSync API by using the API class, calling API.graphql passing in the graphqlOperation helper & the query definition. We the store the data in the restaurants array after it has returned from the array & reset the state.

Now, the data is stored in the state & we can render it to our screen. Let’s update the render method to show the restaurant data in our UI:

// rest of class omitted
render() {
    return (
      <div className="App">
        {
          this.state.restaurants.map((rest, i) => (
            <div style={styles.item}>
              <p style={styles.name}>{rest.name}</p>
              <p style={styles.description}>{rest.description}</p>
            </div>
          ))
        }
      </div>
    );
  }
}

const styles = {
  item: {
    padding: 10,
    borderBottom: '2px solid #ddd'
  },
  name: { fontSize: 22 },
  description: { color: 'rgba(0, 0, 0, .45)' }
}

If you’d like a video walkthrough of how to install & configure the AWS Amplify CLI, click here. Now that the CLI is installed & configured, we can initialize a new Amplify project in our React application. From the inside your React application, run the following command:
Now, we should be able to run the app & see the queried data rendered to our screen:

npm start

GraphQL Mutations

Now that we know how to query for data, let’s look at how to submit mutations & create new items in our API. To do so, we’ll create a form with an input that will create new items in our API & display them to the screen.

To do so, we’ll need to do 5 things:

  1. Import the APIs from AWS Amplify
  2. Import the query definition
  3. Create initial state to hold the restaurants array
  4. Query for the restaurants array from our API & update the state
  5. Display the restaurants array in the React application.

First, we’ll import the mutation:

import { createRestaurant } from './graphql/mutations'

Next, we’ll update the state to hold the restaurant name & description:

 state = { name: '', description: '', restaurants: [] }

Now, we’ll create the two methods we’ll need to handle user input & call the API to create a restaurant:

onChange = e => {
  this.setState({ [e.target.name]: e.target.value })
}
createRestaurant = async () => {
  const { name, description } = this.state
  if (name === '' || description === '') return
  try {
    const restaurant = { name, description }
    const restaurants = [...this.state.restaurants, restaurant]
    this.setState({ restaurants, name: '', description: '' })
    await API.graphql(graphqlOperation(createRestaurant, {input: restaurant}))
    console.log('restaurant successfully created!')
  } catch (err) {
    console.log('error: ', err)
  }
}

In the createRestaurant method we do 5 things:

  1. Import the APIs from AWS Amplify
  2. Import the query definition
  3. Create initial state to hold the restaurants array
  4. Query for the restaurants array from our API & update the state
  5. Display the restaurants array in the React application.

Finally, we’ll create the UI for the form fields & button to call the createRestaurant method:

<div style={styles.inputContainer}>
  <input
    name='name'
    placeholder='restaurant name'
    onChange={this.onChange}
    value={this.state.name}
    style={styles.input}
  />
  <input
    name='description'
    placeholder='restaurant description'
    onChange={this.onChange}
    value={this.state.description}
    style={styles.input}
  />
</div>
<button
  style={styles.button}
  onClick={this.createRestaurant}
>Create Restaurant</button>

const styles = {
  // existing styles omitted
  inputContainer: {
    margin: '0 auto', display: 'flex', flexDirection: 'column', width: 300
  },
  button: {
    border: 'none', backgroundColor: '#ddd', padding: '10px 30px'
  },
  input: {
    fontSize: 18,
    border: 'none',
    margin: 10,
    height: 35,
    backgroundColor: "#ddd",
    padding: 8
  }
}

If you’d like a video walkthrough of how to install & configure the AWS Amplify CLI, click here. Now that the CLI is installed & configured, we can initialize a new Amplify project in our React application. From the inside your React application, run the following command:
Now, we should be able to run the app and create new restaurants from the React application:

GraphQL Subscriptions

As I mentioned in the introduction, one of the most powerful features of GraphQL are real-time data with subscriptions.

Subscriptions push data from the server to the clients that choose to listen to the real time data coming from the server. We can subscribe to the creation of a new item, in our case the creation of a new restaurant, & get notified of the creation of the item as well as receive the item data in the subscription callback.

To implement subscriptions, we need to do three things:

  1. Import the APIs from AWS Amplify
  2. Import the query definition
  3. Create initial state to hold the restaurants array
  4. Query for the restaurants array from our API & update the state
  5. Display the restaurants array in the React application.
// import the subscription defition
import { onCreateRestaurant } from './graphql/subscriptions'

// initialize the subscription in componentDidMount
async componentDidMount() {
  // other code from this method omitted
  this.subscription = API.graphql(
    graphqlOperation(onCreateRestaurant)
  ).subscribe({
    next: restaurantData => {
      const restaurant = restaurantData.value.data.onCreateRestaurant
      const restaurants = [
        ...this.state.restaurants.filter(r => {
          return (
            r.name !== restaurant.name && r.description !== restaurant.description
          )
        }),
        restaurant
      ]
      this.setState({ restaurants })
    }
  })
}

// remove the subscription in componentWillUnmount
componentWillUnmount() {
  this.subscription.unsubscribe()
}

When the subscription fires, we do 3 things:

  1. Import the APIs from AWS Amplify
  2. Import the query definition
  3. Create initial state to hold the restaurants array
  4. Query for the restaurants array from our API & update the state
  5. Display the restaurants array in the React application.
Conclusion

In this tutorial we’ve walked through all of the typical GraphQL operations you would use in a real-world application. We’ve also built & deployed a real-world GraphQL API that will automatically scale for you as your application scales.

The only data source we’ve looked at so far is DynamoDB, but AWS AppSync also supports other data sources out of the box (including Amazon ElasticSearch, AWS Lambda, & http resolvers). If you’re interested in learning more about using other data sources, check out the documentation here.

AWS AppSync has 2 JavaScript clients: AWS Amplify (which we have already covered), & the AWS AppSync JavaScript SDK (which we did not cover).

If you are looking for a solution that handles offline data & operations for you, then take a look at the AWS AppSync JavaScript SDK because it’s built in & works out of the box. You can still use Amplify to create & configure the AWS AppSync service while using the AWS AppSync JS SDK to interact with the service.

AWS Training Online Course - Best AWS Certification - Intellipaat

AWS Training Online Course - Best AWS Certification - Intellipaat

Enroll for best AWS training course with AWS Certification online and placement support. Learn Amazon web services AWS course from certified AWS experts. Enroll Now!

Enroll for best AWS training course with AWS Certification online and placement support. Learn Amazon web services AWS course from certified AWS experts. Enroll Now!