I’ve been writing a GraphQL API using the Serverless Framework and apollo-server-lambda, and was wondering if there was a good way to only have authorization enabled on certain queries and mutations.
On a REST API it’s possible to include something like authorizer: aws_iam
inside of the serverless.yml file, but since with GraphQL there’s only one endpoint, the following configuration seems like the only option if you want to allow unauthenticated requests.
functions:
graphql:
handler: graphql.handler
events:
- http:
path: graphql
method: get
cors: true
- http:
path: graphql
method: post
cors: true
As a workaround, I’m sending the JWT tokens directly within GraphQL and verifying them within the Lambda function, but I’m wondering if there’s a simpler solution for this. This, for example, is the query that the client can call to show see what items the user has added to their list.
type ListEntry {
userId: String!
itemId: String!
createdAt: String!
}
type Query {
listEntries(userId: String!, accessToken: String!): [ListEntry]
}
And the resolver is written as follows.
import AWS from 'aws-sdk'
const dynamoDB = new AWS.DynamoDB.DocumentClient()
const resolvers = {
Query: {
listEntries: async (
_: any,
{ userId, accessToken }: { userId: string; accessToken: string }
): Promise<ListEntry[]> => {
const tokenIsValid = await verifyToken(userId, accessToken)
if (!tokenIsValid) {
throw new AuthenticationError('JWT token does not match user ID.')
}
const params = {
TableName: process.env.listEntryTableName as string,
KeyConditionExpression: 'userId = :userId',
ExpressionAttributeValues: {
':userId': userId
}
}
const res = await dynamoDB.query(params).promise()
return res.Items as ListEntry[]
}
}
}
Where the function verifyToken
is
import AWS from 'aws-sdk'
const cognito = new AWS.CognitoIdentityServiceProvider()
export const verifyToken = async (
userId: string,
accessToken: string
): Promise<boolean> => {
const params = {
AccessToken: accessToken
}
const user = await cognito.getUser(params).promise()
return user.Username === userId
}
What’s the best way to go about doing this? When I Google this, all I seem to get is solutions using AWS AppSync, which is not what I want. Thanks!