Skip to content

Gen 2 Data: AppSync Service Role not provisioned with lambdaAuthorizationMode and allow.resource #2775

Open
@tellerj

Description

@tellerj

Environment information

System:
  OS: Windows 11 10.0.26100
  CPU: (24) x64 13th Gen Intel(R) Core(TM) i7-13700KF
  Memory: 3.31 GB / 31.84 GB
Binaries:
  Node: 22.14.0 - C:\Program Files\nodejs\node.EXE
  Yarn: undefined - undefined
  npm: 11.2.0 - C:\Program Files\nodejs\npm.CMD
  pnpm: 10.7.1 - ~\AppData\Local\pnpm\pnpm.CMD
NPM Packages:
  @aws-amplify/ai-constructs: Not Found
  @aws-amplify/auth-construct: Not Found
  @aws-amplify/backend: 1.16.0
  @aws-amplify/backend-ai: Not Found
  @aws-amplify/backend-auth: Not Found
  @aws-amplify/backend-cli: 1.7.0
  @aws-amplify/backend-data: Not Found
  @aws-amplify/data-construct: Not Found
  @aws-amplify/data-schema: Not Found
  @aws-amplify/deployed-backend-client: Not Found
  @aws-amplify/form-generator: Not Found
  @aws-amplify/model-generator: Not Found
  @aws-amplify/platform-core: Not Found
  @aws-amplify/plugin-types: Not Found
  @aws-amplify/sandbox: Not Found
  @aws-amplify/schema-generator: Not Found
  @aws-cdk/toolkit-lib: Not Found
  aws-amplify: 6.14.3
  aws-cdk-lib: 2.192.0
  typescript: 5.8.3
No AWS environment variables
No CDK environment variables

Describe the bug

When configuring an Amplify Gen 2 Data resource with a custom Lambda authorizer using lambdaAuthorizationMode and granting AppSync permission to invoke it via schema.authorization(allow => [allow.resource(authorizerHandler)]), the necessary IAM Service Role for the AppSync API is not being created or assigned during deployment (ampx sandbox).

Symptoms:

  1. After a seemingly successful ampx sandbox deployment (no errors reported by the CLI), navigating to the deployed AppSync API settings in the AWS console shows the "Role" field under "API configurations -> Logging" is empty.
    Image
  2. The configured Lambda authorizer function is never invoked when making API calls to operations protected by allow.custom(). This is confirmed by the fact that the corresponding CloudWatch Log Group for the authorizer Lambda is never created, even though the Lambda function itself exists and its execution role has the necessary logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents permissions.

Image

  1. Consequently, API calls to operations using allow.custom() fail with an "Unauthorized" error message directly from AppSync, as the authorization check cannot be performed.
  2. Analysis of the CloudFormation stacks shows that the stack responsible for the Lambda function correctly creates the AWS::Lambda::Permission resource allowing AppSync invocation. However, the CloudFormation stack responsible for the AWS::AppSync::GraphQLApi resource does not contain an AWS::IAM::Role resource for the AppSync service itself.This suggests Amplify is failing to provision the AppSync service role required for it to assume permissions, including the permission to invoke the configured Lambda authorizer.

Image

Image

Reproduction steps

  1. Initialize Amplify Backend: Create a basic Amplify Gen 2 project with Auth and Data.
    npm create amplify@latest my-app --yes
    cd my-app
  2. Define Auth: Configure basic email/password auth in amplify/auth/resource.ts.
    // amplify/auth/resource.ts
    import { defineAuth } from '@aws-amplify/backend';
    
    export const auth = defineAuth({
      loginWith: {
        email: true,
      },
    });
  3. Define Minimal Lambda Authorizer: Create a placeholder Lambda function.
    • amplify/data/my-authorizer/resource.ts:
      import { defineFunction } from "@aws-amplify/backend";
      export const myAuthorizerHandler = defineFunction({
          name: 'my-test-authorizer',
          entry: './handler.ts',
      });
    • amplify/data/my-authorizer/handler.ts:
      import type { AppSyncAuthorizerHandler, AppSyncAuthorizerResult } from 'aws-lambda';
      export const handler: AppSyncAuthorizerHandler = async (event): Promise<AppSyncAuthorizerResult> => {
        console.log(`EVENT: ${JSON.stringify(event)}`);
        // Basic allow for testing invocation
        return { isAuthorized: true };
      };
  4. Define Data with Lambda Authorizer: Configure amplify/data/resource.ts to use the Lambda authorizer.
    // amplify/data/resource.ts
    import { a, defineData, type ClientSchema } from '@aws-amplify/backend';
    import { myAuthorizerHandler } from './my-authorizer/resource'; // Import the handler
    
    const schema = a.schema({
      Todo: a.model({
        content: a.string(),
      })
      // Use allow.custom()
      .authorization(allow => [allow.custom()]),
    
    // Grant AppSync permission to invoke the Lambda
    }).authorization((allow) => [
      allow.resource(myAuthorizerHandler),
    ]);
    
    export type Schema = ClientSchema<typeof schema>;
    
    // Configure the data resource with the Lambda authorizer
    export const data = defineData({
      schema,
      authorizationModes: {
        defaultAuthorizationMode: 'userPool', // Use Cognito User Pool as default
        lambdaAuthorizationMode: {
          function: myAuthorizerHandler, // Link the function
          timeToLiveInSeconds: 300,
        },
      },
    });
  5. Define Backend: Ensure amplify/backend.ts includes auth and data.
    // amplify/backend.ts
    import { defineBackend } from '@aws-amplify/backend';
    import { auth } from './auth/resource';
    import { data } from './data/resource';
    
    export const backend = defineBackend({
      auth,
      data,
    });
  6. Deploy Sandbox: Run npx ampx sandbox. Wait for deployment to complete.
  7. Check AppSync Settings: Go to the AWS AppSync console, find the deployed API (amplifyData), go to Settings. Observe that the "Role" field under "API configurations" is empty (-).
  8. Attempt API Call: From a client application authenticated with Cognito, attempt an operation on the Todo model (e.g., client.models.Todo.list()).
  9. Observe Error: The call will fail with an "Unauthorized" error message from AppSync.
  10. Check CloudWatch: Go to CloudWatch Logs and look for the log group associated with the my-test-authorizer Lambda. Observe that the log group does not exist, indicating the Lambda was never invoked.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions