Page updated Apr 19, 2024

Custom data access using Lambda functions

You can define your own custom authorization rule with a Lambda function.

amplify/data/resource.ts
1import {
2 type ClientSchema,
3 a,
4 defineData,
5 defineFunction,
6} from '@aws-amplify/backend';
7
8const schema = a.schema({
9 Todo: a
10 .model({
11 content: a.string(),
12 })
13 // STEP 1
14 // Indicate which models / fields should use a custom authorization rule
15 .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 2
25 // Pass in the function to be used for a custom authorization rule
26 lambdaAuthorizationMode: {
27 function: defineFunction({
28 entry: './custom-authorizer.ts',
29 }),
30 // (Optional) STEP 3
31 // Configure the token's time to live
32 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 definition
3
4const client = generateClient<Schema>();
5
6const { errors, data: newTodo } = await client.models.Todo.create(
7 {
8 content: 'My new todo',
9 },
10 {
11 authMode: 'lambda',
12 }
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.ts
2
3// This is sample code. Update this to suite your needs
4import type { AppSyncAuthorizerHandler } from 'aws-lambda'; // types imported from @types/aws-lambda
5
6type ResolverContext = {
7 userid: string;
8 info: string;
9 more_info: string;
10};
11
12export const handler: AppSyncAuthorizerHandler<ResolverContext> = async (
13 event
14) => {
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-checker
24 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: 300
33 };
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 client
3 "requestContext": {
4 "apiId": "aaaaaa123123123example123", # AppSync API ID
5 "accountId": "111122223333", # AWS Account ID
6 "requestId": "f4081827-1111-4444-5555-5cf4695f339f",
7 "queryString": "mutation CreateEvent {...}\n\nquery MyQuery {...}\n", # GraphQL query
8 "operationName": "MyQuery", # GraphQL operation name
9 "variables": {} # any additional variables supplied to the operation
10 }
11}

Your Lambda authorization function needs to return the following JSON:

1{
2 // required
3 "isAuthorized": true, // if "false" then an UnauthorizedException is raised, access is denied
4 "resolverContext": { "banana": "very yellow" }, // JSON object visible as $ctx.identity.resolverContext in VTL resolver templates
5
6 // optional
7 "deniedFields": ["TypeName.FieldName"], // Forces the fields to "null" when returned to the client
8 "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.