AWS AppSync without Authentication
Daniel Bayerlein
Posted on March 10, 2020
There are several ways to authorize your application to interact with the AWS 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 with IAM Roles.
I found many comments on GitHub and Stack Overflow that deal with this issue. But no solution felt good until I found the GitHub repo from dabit3.
AWS AppSync supports AWS_IAM. With the Cognito Identity Pool 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
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
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
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
query LIST_EVENTS {
listEvents {
items {
id
name
date
}
}
}
File: schema.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.
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
## 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.
Posted on March 10, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.