Custom data access using Lambda functions
You can define your own custom authorization rule with a Lambda function.
1import {2 type ClientSchema,3 a,4 defineData,5 defineFunction,6} from '@aws-amplify/backend';7
8const schema = a.schema({9 Todo: a10 .model({11 content: a.string(),12 })13 // STEP 114 // Indicate which models / fields should use a custom authorization rule15 .authorization(allow => [allow.custom()]),16});17
18export type Schema = ClientSchema<typeof schema>;19
20export const data = defineData({21 schema,22 authorizationModes: {23 defaultAuthorizationMode: 'lambda',24 // STEP 225 // Pass in the function to be used for a custom authorization rule26 lambdaAuthorizationMode: {27 function: defineFunction({28 entry: './custom-authorizer.ts',29 }),30 // (Optional) STEP 331 // Configure the token's time to live32 timeToLiveInSeconds: 300,33 },34 },35});
In your application, you can perform CRUD operations against the model using client.models.<model-name>
with the lambda
auth mode.
1import { generateClient } from 'aws-amplify/data';2import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition3
4const client = generateClient<Schema>();5
6const { errors, data: newTodo } = await client.models.Todo.create(7 {8 content: 'My new todo',9 },13);
The Lambda function of choice will receive an authorization token from the client and execute the desired authorization logic. The AppSync GraphQL API will receive a payload from Lambda after invocation to allow or deny the API call accordingly.
To configure a Lambda function as the authorization mode, create a new file amplify/data/custom-authorizer.ts
. You can use this Lambda function code template as a starting point for your authorization handler code:
1// amplify/data/custom-authorizer.ts2
3// This is sample code. Update this to suite your needs4import type { AppSyncAuthorizerHandler } from 'aws-lambda'; // types imported from @types/aws-lambda5
6type ResolverContext = {7 userid: string;8 info: string;9 more_info: string;10};11
12export const handler: AppSyncAuthorizerHandler<ResolverContext> = async (13 event14) => {15 console.log(`EVENT: ${JSON.stringify(event)}`);16 const {17 authorizationToken,18 requestContext: { apiId, accountId }19 } = event;20 const response = {21 isAuthorized: authorizationToken === 'custom-authorized',22 resolverContext: {23 // eslint-disable-next-line spellcheck/spell-checker24 userid: 'user-id',25 info: 'contextual information A',26 more_info: 'contextual information B'27 },28 deniedFields: [29 `arn:aws:appsync:${process.env.AWS_REGION}:${accountId}:apis/${apiId}/types/Event/fields/comments`,30 `Mutation.createEvent`31 ],32 ttlOverride: 30033 };34 console.log(`RESPONSE: ${JSON.stringify(response, null, 2)}`);35 return response;36};
You can use the template above as a starting point for your custom authorization rule. The authorization Lambda function receives the following event:
1{2 "authorizationToken": "ExampleAuthToken123123123", # Authorization token specified by client3 "requestContext": {4 "apiId": "aaaaaa123123123example123", # AppSync API ID5 "accountId": "111122223333", # AWS Account ID6 "requestId": "f4081827-1111-4444-5555-5cf4695f339f",7 "queryString": "mutation CreateEvent {...}\n\nquery MyQuery {...}\n", # GraphQL query8 "operationName": "MyQuery", # GraphQL operation name9 "variables": {} # any additional variables supplied to the operation10 }11}
Your Lambda authorization function needs to return the following JSON:
1{2 // required3 "isAuthorized": true, // if "false" then an UnauthorizedException is raised, access is denied4 "resolverContext": { "banana": "very yellow" }, // JSON object visible as $ctx.identity.resolverContext in VTL resolver templates5
6 // optional7 "deniedFields": ["TypeName.FieldName"], // Forces the fields to "null" when returned to the client8 "ttlOverride": 10 // The number of seconds that the response should be cached for. Overrides default specified in "amplify update api"9}
Review the Amplify documentation to set the custom authorization token for the Data client.