AWS AppSync without Authentication
Tags: aws, appsync, graphql, serverless
Published: 2020-03-10 09:26:56

There are several ways to authorize your application to interact with the [AWS AppSync](https://aws.amazon.com/appsync/) GraphQL API. But what if your application has no authentication? This post shows you a best practice for communicating with AWS AppSync for public web sites.

**TL;DR** - Use [Cognito Identity Pools](https://docs.aws.amazon.com/cognito/latest/developerguide/identity-pools.html) with [IAM Roles](https://docs.aws.amazon.com/cognito/latest/developerguide/iam-roles.html).

I found many comments on [GitHub](https://github.com/) and [Stack Overflow](https://stackoverflow.com/) that deal with this issue. But no solution felt good until I found the [GitHub repo](https://github.com/dabit3/appsync-auth-and-unauth) from [dabit3](https://github.com/dabit3).

AWS AppSync supports [AWS_IAM](https://docs.aws.amazon.com/en_en/appsync/latest/devguide/security.html#aws-iam-authorization). With the [Cognito Identity Pool](https://docs.aws.amazon.com/cognito/latest/developerguide/identity-pools.html) you can associate the IAM policy.

## Code, Code and Code

In the following two steps I explain which changes are necessary.

### Step 1: Configure AWS AppSync

The first step is to specify the authentication type in `aws-exports.js`. Set the `authenticationType` to `'AWS_IAM'`.

*File:* `aws-exports.js`

```js
export default {
  graphqlEndpoint: process.env.AWS_APPSYNC_GRAPHQL_ENDPOINT,
  region: 'eu-central-1',
  authenticationType: 'AWS_IAM'
}
```

Update the AppSync configuration by setting `credentials` to `() => Auth.currentCredentials()`.

*File:* `index.js`

```js
import React from 'react'
import ReactDOM from 'react-dom'
import Auth from '@aws-amplify/auth'
import AWSAppSyncClient from 'aws-appsync'
import { ApolloProvider } from '@apollo/client'
import { Rehydrated } from 'aws-appsync-react'

import App from './App'
import AppSyncConfig from './aws-exports'

const appSyncConfig = {
  url: AppSyncConfig.graphqlEndpoint,
  region: AppSyncConfig.region,
  auth: {
    type: AppSyncConfig.authenticationType,
    credentials: () => Auth.currentCredentials()
  },
  disableOffline: true // https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/102
}

const appSyncOptions = {
  defaultOptions: {
    watchQuery: {
      fetchPolicy: 'cache-and-network'
    }
  }
}

const client = new AWSAppSyncClient(appSyncConfig, appSyncOptions)

ReactDOM.render(
  <ApolloProvider client={client}>
    <Rehydrated>
      <App />
    </Rehydrated>
  </ApolloProvider>,
  document.getElementById('app')
)

```

### Not so important

The rest of the following code is only for the completeness of the demo application.

*File:* `App.js`

```js
import React from 'react'
import { Query } from '@apollo/client/react/components'
import Auth from '@aws-amplify/auth'

import { LIST_EVENTS } from './graphql/queries.gql'

Auth.configure({
  region: process.env.AWS_COGNITO_REGION,
  identityPoolId: process.env.AWS_COGNITO_IDENTITIY_POOL_ID
})

export default () => (
  <>
    <h1>Events</h1>
    <Query query={LIST_EVENTS}>
      {({ loading, error, data }) => (
        data.listEvents.items.map(event => (
          <div key={`event_${event.id}`}>
            <h2>{event.name}</h2>
            <p>{event.date}</p>
          </div>
        ))
      )}
    </Query>
  </>
)
```

*File:* `queries.gql`

```graphql
query LIST_EVENTS {
  listEvents {
    items {
      id
      name
      date
    }
  }
}
```

*File:* `schema.graphql`

```graphql
type Event {
  id: ID!
  name: String!
  date: AWSDateTime!
}

type ModelEventConnection {
  items: [Event]
  nextToken: String
}

type Query {
  listEvents(
    limit: Int,
    nextToken: String
  ): ModelEventConnection
}

schema {
  query: Query
}
```

### Step 2: Setup Cognito Identity Pool

In my example I use the [Serverless Framework](https://serverless.com/).

First we define the Cognito Identity Pool and set `AllowUnauthenticatedIdentities: true`. This enable access for unauthenticated identities.

In the second part I link the role for the Identity Pool. BTW: You can also set an role for authenticated users via `authenticated` if your application supports authenticated and unauthenticated users.

At the last step, I create the IAM role and the related policy. Add your resources (`Query`, `Mutation`, `Subscription`) to the policy.

*File:* `serverless.yml`

```yaml
## Cognito Identity Pool
CognitoIdentityPool:
  Type: AWS::Cognito::IdentityPool
  Properties:
    IdentityPoolName: ${self:service}-${self:provider.stage}-${self:provider.region}-IdentityPool
    AllowUnauthenticatedIdentities: true

## IAM roles
CognitoIdentityPoolRoles:
  Type: AWS::Cognito::IdentityPoolRoleAttachment
  Properties:
    IdentityPoolId:
      Ref: CognitoIdentityPool
    Roles:
      unauthenticated:
        !GetAtt CognitoUnAuthRole.Arn

## IAM role used for unauthenticated users
CognitoUnAuthRole:
  Type: AWS::IAM::Role
  Properties:
    Path: /
    AssumeRolePolicyDocument:
      Version: '2012-10-17'
      Statement:
        - Effect: 'Allow'
          Principal:
            Federated: 'cognito-identity.amazonaws.com'
          Action:
            - 'sts:AssumeRoleWithWebIdentity'
          Condition:
            StringEquals:
              'cognito-identity.amazonaws.com:aud':
                Ref: CognitoIdentityPool
            'ForAnyValue:StringLike':
              'cognito-identity.amazonaws.com:amr': unauthenticated
    Policies:
      - PolicyName: ${self:service}-${self:provider.stage}-${self:provider.region}-AppSyncCognitoPolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: 'Allow'
              Action:
                - 'mobileanalytics:PutEvents'
                - 'cognito-sync:*'
                - 'cognito-identity:*'
              Resource: '*'
            - Effect: Allow
              Action:
                - appsync:GraphQL
              Resource:
                !Join [ '', [ !GetAtt GraphQlApi.Arn, '/types/Query/fields/listEvents' ] ]
```

## Ready

🏁 Now you have access to AWS AppSync and the `listEvents` query can be executed without authentication.