Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cognito Trigger templates' return and permissions issue #7582

Open
4 tasks done
josefaidt opened this issue Jun 22, 2021 · 22 comments · Fixed by #9741
Open
4 tasks done

Cognito Trigger templates' return and permissions issue #7582

josefaidt opened this issue Jun 22, 2021 · 22 comments · Fixed by #9741
Assignees
Labels
auth Issues tied to the auth category of the CLI bug Something isn't working p2

Comments

@josefaidt
Copy link
Contributor

josefaidt commented Jun 22, 2021

Before opening, please confirm:

  • I have installed the latest version of the Amplify CLI (see above), and confirmed that the issue still persists.
  • I have searched for duplicate or closed issues.
  • I have read the guide for submitting bug reports.
  • I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.

How did you install the Amplify CLI?

yarn

If applicable, what version of Node.js are you using?

16.3

Amplify CLI Version

5.0.1

What operating system are you using?

Mac

Amplify Categories

auth

Amplify Commands

Not applicable

Describe the bug

Return Issue

Trigger's index.js file calls module handlers by passing handler(event, context, callback) without a return, and inside the "module" file (i.e. add-to-group.js) callback is not utilized and response is not returned.

message: "Invalid lambda function output : Invalid JSON"
__type: "InvalidLambdaResponseException"

Permission Issue

In the add-to-group Cognito trigger template, it makes a call to create a group, however these permissions are missing #7576 (comment)

Expected behavior

Template trigger files should return out-of-the-box and appropriate permissions are attached.

Reproduction steps

(follow steps in issues noted above)

GraphQL schema(s)

# Put schemas below this line

Log output

# Put your logs below this line


Additional information

No response

@HieronymusLex
Copy link

is there a suggested workaround for this issue that doesn't involve the console?

@jimjoes
Copy link

jimjoes commented Feb 1, 2022

Me too. Presumably a workaround is possible while we wait for this to be merged?

@josefaidt
Copy link
Contributor Author

@Straubulous this is still affecting the add-to-group post-confirmation trigger, where we are missing cognito-idp:CreateGroup permissions

PostConfirmation failed with error User: arn:aws:sts::xxx:assumed-role/75825b271e0d5b271e0dPostConfirmation-dev/75825b271e0d5b271e0dPostConfirmation-dev is not authorized to perform: cognito-idp:CreateGroup on resource: arn:aws:cognito-idp:us-east-1:xxx:userpool/us-east-1_CDz4HzvKw because no identity-based policy allows the cognito-idp:CreateGroup action.

@josefaidt
Copy link
Contributor Author

this was accidentally closed by PR merge, one outstanding item here, see #7582 (comment)

@Kirnos
Copy link

Kirnos commented Feb 22, 2022

The issue described in #7582 (comment) is still not resolved and the ticket is closed. Please reopen and fix the remaining item!!!
Looks like a regression as I have never had this problem with some prior versions of Amplify CLI that I was using.

@biztems
Copy link

biztems commented Feb 25, 2022

Same issue here with Amplify CLI v7.6.21.

@scottmcmaster
Copy link

I "solved" this by modifying custom-policies.json as follows:

[
  {
    "Effect": "Allow",
    "Action": [
      "cognito-idp:CreateGroup",
      "cognito-idp:AdminAddUserToGroup"
    ],
    "Resource": [
      "<MY HARD-CODED USERPOOL ARN FROM THE ERROR MESSAGE>"
    ]
  }
]

@acusti
Copy link

acusti commented Mar 15, 2022

@scottmcmaster’s workaround worked for me, though i had to also add "cognito-idp:GetGroup" to the Action array:

[
  {
    "Action": [
      "cognito-idp:AdminAddUserToGroup",
      "cognito-idp:CreateGroup",
      "cognito-idp:GetGroup"
    ],
    "Resource": [
      "<HARD-CODED USERPOOL ARN FROM THE ERROR MESSAGE>"
    ]
  }
]

it would solve it for me completely if i could use a string as the resource ARN that would switch between my dev environment userpool ARN and my main environment userpool ARN. does anyone know if that is possible using custom-policies.json?

@saschacontes
Copy link

Unfortunately still unsolved with latest amplify cli 8.1.0

@jedwardblack
Copy link

jedwardblack commented May 22, 2022

@acusti Since "Resource" is an array, you can just add each of your user pool ARNs in that array.

[
  {
    "Action": [
      "cognito-idp:AdminAddUserToGroup",
      "cognito-idp:CreateGroup",
      "cognito-idp:GetGroup"
    ],
    "Resource": [
      "<DEV USERPOOL ARN>",
      "<MAIN USERPOOL ARN>",
    ]
  }
]

@kylekirkby
Copy link
Contributor

@josefaidt any update on this one?

@mlg87
Copy link

mlg87 commented Jul 27, 2022

people have been reporting this very core issue for over two years and the best solutions are either hard coding arns into custom-policies.json or this legend's workaround to get a policy that has the userpool id attached after the fact? is there any test coverage for this part of amplify? has anyone ever UAT'd it? adding a user to a group post confirmation cannot work given the current (today and the last several years) implementation

@proevilz
Copy link

I "solved" this by modifying custom-policies.json as follows:

[
  {
    "Effect": "Allow",
    "Action": [
      "cognito-idp:CreateGroup",
      "cognito-idp:AdminAddUserToGroup"
    ],
    "Resource": [
      "<MY HARD-CODED USERPOOL ARN FROM THE ERROR MESSAGE>"
    ]
  }
]

Is there anyway to have it automatically fill in the resource ARN? My project is open source which means I'm going to have to give extra instructions on doing this part.

Seems like this issue has been open for a few years now. Any update from the Amplify team??

@jerocosio
Copy link

I'm having the same issue with the circular dependencies, but trying to get access to a DynamoDB table with still no luck, only getting errors when trying to deploy.

I think it's extremely bad that this issues has been around since 2019 and there's still no actual fix in the roadmap, we need to do this 'hacky' way to do things as simple as reading from a database before a user signs-up.

Even the official documentation has a way of doing this use case, but this wouldn't work on a real world scenario as the selected triggers through the console will be overwritten every time you update any other. I think this should be prioritized as it renders the trigger functions basically useless except for really basic operations like sending e-mails.

@tomburge
Copy link

tomburge commented Dec 28, 2022

This issue is still not fixed and I'm on Amplify CLI version 10.5.2. I'm having to add a custom policy for the post confirmation lambda since the lambda only has CloudWatch permissions from the lambda execution role. The lambda trigger that invokes this one has the right permissions.

amplify error 1
amplify permissions 1

Here is the configuration I used:

C:\repos\tom\amplify-model>amplify add auth
Using service: Cognito, provided by: awscloudformation

The current configured provider is Amazon Cognito.

Do you want to use the default authentication and security configuration? Manual configuration
Select the authentication/authorization services that you want to use: User Sign-Up & Sign-In only (Best used with a cloud API only)
Provide a friendly name for your resource that will be used to label this category in the project: amplifymodela273a5dda273a5dd
Provide a name for your user pool: amplifymodela273a5dd_userpool_a273a5dd
Warning: you will not be able to edit these selections.
How do you want users to be able to sign in? Email
Do you want to add User Pool Groups? Yes
? Provide a name for your user pool group: users
? Do you want to add another User Pool Group Yes
? Provide a name for your user pool group: admins
? Do you want to add another User Pool Group No
√ Sort the user pool groups in order of preference · admins, users
Do you want to add an admin queries API? No
Multifactor authentication (MFA) user login options: OPTIONAL (Individual users can use MFA)
For user login, select the MFA types: Time-Based One-Time Password (TOTP)
Specify an SMS authentication message: Your authentication code is {####}
Email based user registration/forgot password: Enabled (Requires per-user email entry at registration)
Specify an email verification subject: Your verification code
Specify an email verification message: Your verification code is {####}
Do you want to override the default password policy for this User Pool? No
Warning: you will not be able to edit these selections.
What attributes are required for signing up? Email
Specify the app's refresh token expiration period (in days): 30
Do you want to specify the user attributes this app can read and write? Yes
Specify read attributes: Address, Email, Family Name, Locale, Given Name, Updated At, Zone Info, Email Verified?
Specify write attributes: Address, Family Name, Locale, Given Name, Updated At, Zone Info
Do you want to enable any of the following capabilities? Add User to Group
Do you want to use an OAuth flow? No
? Do you want to configure Lambda Triggers for Cognito? Yes
? Which triggers do you want to enable for Cognito Post Confirmation
? What functionality do you want to use for Post Confirmation Add User To Group
√ Enter the name of the group to which users will be added. · users
Successfully added resource amplifymodela273a5dda273a5ddPostConfirmation locally.

Next steps:
Check out sample function code generated in /amplify/backend/function/amplifymodela273a5dda273a5ddPostConfirmation/src
"amplify function build" builds all of your functions currently in the project
"amplify mock function " runs your function locally
To access AWS resources outside of this Amplify app, edit the C:\repos\tom\amplify-model\amplify\backend\function\amplifymodela273a5dda273a5ddPostConfirmation\custom-policies.json
"amplify push" builds all of your local backend resources and provisions them in the cloud
"amplify publish" builds all of your local backend and front-end resources (if you added hosting category) and provisions them in the cloud
Successfully added the Lambda function locally
? Do you want to edit your add-to-group function now? Yes
Edit the file in your editor: C:\repos\tom\amplify-model\amplify\backend/function/amplifymodela273a5dda273a5ddPostConfirmation/src/add-to-group.js
? Press enter to continue
✅ Successfully added auth resource amplifymodela273a5dda273a5dd locally

✅ Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud

C:\repos\tom\amplify-model>

This is the custom policy I added:

{
"Action": [
"cognito-idp:GetGroup",
"cognito-idp:AdminAddUserToGroup",
"cognito-idp:CreateGroup"
],
"Resource": [
"arn:aws:cognito-idp:us-east-1:466252738153:userpool/us-east-1_BI9MCZG5u"
]
}

This function has these permissions now and completes successfully without error:

amplify permissions 2

This is what needs to be added as a fix.

@jayantwalvekar
Copy link

I have this problem and it involves a lot of manual work. For fresh backend deployment, do amplify push first, update this file with ARN and do another push. When team members deploy their own backend, they have to modify the custom-policies.json file with their ARN and remember not to check it in and back it up during branch switching, etc. It would really help to have this happen automatically when we allow the lambda function to access other resources.

@josefaidt josefaidt assigned josefaidt and unassigned akshbhu Aug 29, 2023
@OperationalFallacy
Copy link

Amplify team, wanted to ask what's your approach to semi-permanent bugs like this, when Cognito trigger Lambdas can not access GraphQL API? Like everybody else, I've run into number of these, I slowly find and implement workarounds - great its there... somewhere. And sometimes they are in triple-chained closed issues like this one.

This is however a very inefficient process of development. These issues been around for so long time that maybe you can get fixed links and the solutions discovered more easily? I'd rather go via official docs that links outstanding ticket that comb through outstanding ticket trying to find a solution to a common problem

@timheilman
Copy link

I've just run into this issue not with the Amplify CLI and backend hosting, but with AWS CDK. What a tricky issue!

@lbowes
Copy link

lbowes commented Mar 25, 2024

This needs fixing urgently.

@timheilman
Copy link

timheilman commented Mar 25, 2024

FWIW, I fixed this by breaking the cyclic dependency out of the (in my case, not amplify, but CDK, stack), then slurping the stack outputs into environment variables, then consuming those in a script to use the AWS JS SDK v3 to complete the cycle of permissions.

Put it on the pile of workarounds! I hope this helps someone.

stackOutputsForDotenv.ts:

import { exec } from "child_process";

import { stackName } from "../bin/yellowbrik-jobs-cloud";
import { logFn } from "../lib/logging";
import { profile, region, stage } from "../lib/requiredEnvVar";
const log = logFn("ts-node-scripts.stackOutputsForDotenv.");
const camelToScreamingSnake = (str: string): string => {
  return str.replace(/([a-z])([A-Z])/g, "$1_$2").toUpperCase();
};

const getOutputs = (stdout: string) => {
  try {
    return JSON.parse(stdout) as {
      OutputKey: string;
      OutputValue: string;
    }[];
  } catch (e) {
    log("jsonParseOfStdout", "error", { e, stdout });
    return [];
  }
};

exec(
  `aws cloudformation describe-stacks --stack-name ${stackName}-${stage()} --query 'Stacks[].Outputs[]' --profile ${profile()} --region us-east-2`,
  (error, stdout /* , stderr */) => {
    if (error) {
      log("awsCloudformationDescribeStacks.error", "error", {
        error,
        stackName,
        stage: stage(),
        profile: profile(),
        region: region(),
      });
      return;
    }
    console.log(
      getOutputs(stdout)
        .map((kv) => `${camelToScreamingSnake(kv.OutputKey)}=${kv.OutputValue}`)
        .join("\n"),
    );
  },
);

addDependenciesToCognitoTriggerLambda.ts:

import { config as dotenvConfig } from "dotenv";
dotenvConfig({ override: true });

import { logFn } from "../lib/logging";
import {
  apiArn,
  apiUrl,
  lambdaNameCognitoPostConfirmation,
  lambdaRoleNameCognitoPostConfirmation,
  userPoolAppClient,
  userPoolArn,
  userPoolId,
} from "../lib/requiredEnvVar";
import { addInlinePolicy } from "../test/iam";
import { addEnvVars } from "../test/lambda";
const log = logFn(
  "ts-node-scripts.addAppSyncDependenciesToCognitoTriggerLambdas.",
);
addEnvVars({
  kvs: {
    API_URL: apiUrl(),
    USER_POOL_ID: userPoolId(),
    USER_POOL_APP_CLIENT:
      userPoolAppClient(),
  },
  lambdaName: lambdaNameCognitoPostConfirmation(),
})
  .then(() => {
    log("addEnvVars.success", "info");
  })
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
  .catch((e) => log("addEnvVars.error", "error", { e }));

addInlinePolicy({
  existingRoleName: lambdaRoleNameCognitoPostConfirmation(),
  newPolicyName: "cognitoPostConfirmationGqlAccessPolicy",
  actions: ["appsync:GraphQL"],
  resources: [`${apiArn()}/types/Mutation/fields/createJobSeekerProfile`],
})
  .then(() => {
    log("addInlinePolicy.graphQl.success", "info");
  })
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
  .catch((e) => log("addInlinePolicy.graphQl.error", "error", { e }));

addInlinePolicy({
  existingRoleName: lambdaRoleNameCognitoPostConfirmation(),
  newPolicyName: "cognitoPostConfirmationCognitoAccessPolicy",
  actions: ["cognito-idp:AdminAddUserToGroup"],
  resources: [userPoolArn()],
})
  .then(() => {
    log("addInlinePolicy.cognito.success", "info");
  })
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
  .catch((e) => log("addInlinePolicy.cognito.error", "error", { e }));

lambda.ts:

import {
  GetFunctionConfigurationCommand,
  LambdaClient,
  UpdateFunctionConfigurationCommand,
} from "@aws-sdk/client-lambda";
import { fromSSO } from "@aws-sdk/credential-providers";

import { logFn } from "../lib/logging";
import { profile, region } from "../lib/requiredEnvVar";
const log = logFn("test.lambda.");

const lambdaClient = new LambdaClient({
  region: region(),
  credentials: fromSSO({ profile: profile() }),
});
export const addEnvVars = async ({
  kvs,
  lambdaName,
}: {
  kvs: Record<string, string>;
  lambdaName: string;
}) => {
  const priorConfig = await lambdaClient.send(
    new GetFunctionConfigurationCommand({ FunctionName: lambdaName }),
  );
  const priorEnvironment = priorConfig.Environment?.Variables ?? {};
  const input = {
    FunctionName: lambdaName,
    Environment: {
      Variables: {
        ...priorEnvironment,
        ...kvs,
      },
    },
  };
  log("lambdaClientSend", "debug", { input });
  await lambdaClient.send(new UpdateFunctionConfigurationCommand(input));
};

iam.ts:

import { IAMClient, PutRolePolicyCommand } from "@aws-sdk/client-iam";
import { fromSSO } from "@aws-sdk/credential-providers";

import { profile, region } from "../lib/requiredEnvVar";

const iamClient = new IAMClient({
  region: region(),
  credentials: fromSSO({ profile: profile() }),
});

export const addInlinePolicy = async ({
  actions,
  resources,
  existingRoleName,
  newPolicyName,
}: {
  actions: string[];
  resources: string[];
  existingRoleName: string;
  newPolicyName: string;
}) => {
  await iamClient.send(
    new PutRolePolicyCommand({
      RoleName: existingRoleName,
      PolicyName: newPolicyName,
      PolicyDocument: JSON.stringify({
        Version: "2012-10-17",
        Statement: { Effect: "Allow", Action: actions, Resource: resources },
      }),
    }),
  );
};

@timheilman
Copy link

I think I found another instance of such circular dependencies: when using CDK to both define a lambda as a DynamoDB stream event handler that tries to write to an OpenSearch Serverless collection defined in the same CDK stack, the collection's data access policy must have the lambda's ARN for the Principal of "aoss:*" permissions on its collection and index, whereas an inline policy on the lambda must have the collection's ARN for the resource of the "aoss:APIAccessAll" permission. So this isn't exclusively a Amplify issue, nor even exclusively a Cognito triggers issue. There are just some cases where your underlying CloudFormation stack will have circular dependencies, and there's no generic way yet to resolve that.

@biller-aivy
Copy link

The solution could be a second sequence of deploying with some dependencies, defined in a separate circular dependency file?

The cli is checking for circular dependencies, write it to a separate file, deploy, if the file exists run the second deploy and delete the file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auth Issues tied to the auth category of the CLI bug Something isn't working p2
Projects
None yet