amplify-cli
amplify-cli copied to clipboard
Cognito Trigger templates' return and permissions issue
Before opening, please confirm:
- [X] I have installed the latest version of the Amplify CLI (see above), and confirmed that the issue still persists.
- [X] I have searched for duplicate or closed issues.
- [X] I have read the guide for submitting bug reports.
- [X] 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"
- cognito add-to-user trigger https://github.com/aws-amplify/amplify-cli/issues/7576#issuecomment-865924364
- mock-related https://github.com/aws-amplify/amplify-cli/issues/7531
- https://github.com/aws-amplify/amplify-cli/issues/7179
Permission Issue
In the add-to-group Cognito trigger template, it makes a call to create a group, however these permissions are missing https://github.com/aws-amplify/amplify-cli/issues/7576#issuecomment-865912228
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
is there a suggested workaround for this issue that doesn't involve the console?
Me too. Presumably a workaround is possible while we wait for this to be merged?
@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.
this was accidentally closed by PR merge, one outstanding item here, see https://github.com/aws-amplify/amplify-cli/issues/7582#issuecomment-1042372502
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.
Same issue here with Amplify CLI v7.6.21.
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>"
]
}
]
@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?
Unfortunately still unsolved with latest amplify cli 8.1.0
@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>",
]
}
]
@josefaidt any update on this one?
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
I "solved" this by modifying
custom-policies.jsonas 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??
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.
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.

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
✅ 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:

This is what needs to be added as a fix.
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.
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
I've just run into this issue not with the Amplify CLI and backend hosting, but with AWS CDK. What a tricky issue!
This needs fixing urgently.
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 },
}),
}),
);
};
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.
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.