amazon-web-servicesaws-lambdaamazon-cognitoaws-amplify

Amplify Gen 2: lambda function `No federated jwt` error


I have an Amplify app using Cognito for userpool authentication. Once a user has signed up and used the code sent via email (all Cognito functionality), they are passed to a custom form where additional information is captured. On submit a lambda function is executed that should:

  1. Create a group in Cognito
  2. Add the user to the group
  3. Create a record in a DynamoDB table and use the group name created in step 1. in a "writeGroups" fields referenced in the schema for allow.groupsDefinedIn('writeGroups')

The lambda function deploys nicely as part of my sandbox, and permissions have been granted for allow.resource(createAccount).to(["manageGroups", "addUserToGroup", "manageGroupMembership"])} as well as modifying DynamoDB data. However as soon as either CognitoIdentityProviderClient or await client.models.User.create(user) are hit from inside the lambda function, I get the following error from Graphql API No federated jwt. Example of code used:

const user = {
  userId: userId,
  ...
};
const { data, errors} = await client.models.User.create(user);   <-- `No federated jwt` error thrown here

Or

const cognitoClient = new CognitoIdentityProviderClient();
const userGroup = {
  GroupName: `GroupName`,
  Precedence: 1,
  Description: `Description`,
  UserPoolId: userPoolId,
};
const createCommand = new CreateGroupCommand(userGroup);
const response = await cognitoClient.send(createCommand );    <-- `No federated jwt` error thrown here

The examples in the Amplify Gen 2 documentation don't cover this scenario. What am I missing?


Solution

  • If the User data model is set to only allow owner, or authenticated users to create then you need to set the authMode for your client.models.user.create command to identityPool and pass in the user's jwtToken. The token can be retrieved from the header if you're using APIGateway to call the lambda. If you're not using API gateway you'll have to pass it in yourself.

    Here's an example data

    const schema = a.schema({
      User: a
        .model({
          name: a.string(),
          group: a.string(),
        })
        .identifier(["User"]),
    })
    .authorization((allow) => [allow.owner()]);
    
    export type Schema = ClientSchema<typeof schema>;
    
    export const data = defineData({
      schema,
      authorizationModes: {
        defaultAuthorizationMode: "identityPool",
      },
    });
    

    And here's an example lambda called using APIGateway

    export const handler: APIGatewayProxyHandler = async (event) => {
    
        let jwtToken: string | undefined;
    
        // Method 1: From JWT authorizer claims
        jwtToken = event.requestContext.authorizer?.jwt?.claims?.['id_token'] as string;
    
        // Method 2: From Authorization header (if token is passed in header)
        if (!jwtToken) {
            const authHeader = event.headers['Authorization'];
            jwtToken = authHeader;
        }
        if (jwtToken) {
            console.log('jwtToken found:', jwtToken);
            // Use the jwtToken as needed
        } else {
            console.log('jwtToken not found in the request');
        }
    
          const client = generateClient<Schema>({
            authMode: "identityPool",
            authToken: jwtToken,
    
          });
          const createStoryResult = await client.models.User.create({
            name: "foo",
            group: "Bar",
            },
          {
            authMode: "identityPool",
            authToken: jwtToken,
          });