Mapping Cognito Identity Id and User Pool Id

Link to chapter - http://serverless-stack.com/chapters/mapping-cognito-identity-id-and-user-pool-id.html

What will be the usage of the UserPool User Id inside the lambda?

I’ve trying to store some user data that wanted to retrieve inside Lambda and couldn’t figure out how to do it and ended up with a dynamodb table that has stored the CognitoIdentityId as a way to retrieve that data.

If this chapter is a solution to my problem I don’t see which part of the JS sdk I have to use inside the lambda to access the user pool.

I think it’s useful for looking up user attributes for instance, if you wanted a subscription/follow feature between users. It appears to me to open up your userpool to be more like a dynamoDB table you can query.

1 Like

It seems so but unfortunately I could find what part of the JS sdk let’s you get that information. Maybe @jayair could give us a hint about what was the meaning of that chapter.

1 Like

Yeah the basic problem is that when you authenticate your Lambda functions with IAM, you only get the Identity Id to work with. But there is no way to get the User Pool Id given the Identity Id. But this chapter details a little trick to let you get that.

You could then use that to get info using the Admin Cognito User Pool - https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminGetUser.html.

@jayair back to this old topic
I have been trying to find how to get username & email for the IAM authenticated user in WS API $connect and this article and forum gave a bit hope. I can extract the UserPoolId and UserPoolUserId, but I still can’t figure out how to get username. AdminGetUser requires to have parameters UserPoolId and Username. So there is no help there? What I am missing?

That’s weird. That AdminGetUser API used to take the user id. Can you try passing that it to see if that works?

I tried already and it worked, yee. That was so confusing, I lost days for looking the answer. All good now. This blog post helped to get to the track, thanks.

1 Like

Hello! thanks for your post and all the website, it helps a lot! I am trying to get the cognito id and user pool id but I get null values for the event.requestContext like:

identity: {
    cognitoIdentityPoolId: null,
    accountId: null,
    cognitoIdentityId: null,
    caller: null,
    sourceIp: ,// my ip
    principalOrgId: null,
    accessKey: null,
    cognitoAuthenticationType: null,
    cognitoAuthenticationProvider: null,
    userArn: null,
    userAgent: 'insomnia',
    user: null
  },

Do you know why could this be happening? thank you!!

Hmm that means that the auth didn’t happen. How are you making this request?

It looks like this method of extracting of the Cognito user pool user ID page (aka “sub” or subject) has become obsolete. I proposed a new approach further below. But first an explanation…

Problem: The following cognitoIdentityId property no longer exists embedded within the event object passed into Cognito-authenticated Lambdas:

event.requestContext.identity.cognitoIdentityId

Gone. This was a reasonable approach seemingly due to Cognito design/API shortcomings, but it has now become obsolete likely due to Cognito refactoring its internals. This may have worked some time ago, however this is an ‘unofficial’ way to fetch user pool user ID (aka “sub” or subject.) Below is an alternative which should presently work.

Solution: I was able to workaround this by injecting the header object with a new header, through an API Gateway endpoint & method integration, with the value for the $context cognitoAuthenticationProvider value, which similarly has the user pool user sub ID embedded inside. Once on the header, you can access it in your Lambda, parse and use.

Implementation:

  1. Add an integration. In the API Gateway management console, for each endpoint and method combination, go to its integration screen. Add a Parameter mapping, of Mapping type “All incoming requests”. The “Parameter to modify” should be something like “header.sub”, Modification type “Overwrite”, Value of “$context.identity.cognitoAuthenticationProvider”
    Save and repeat for all endpoint & method combinations needing access to this subject user ID.

The downside is this integration needs to be manually added in the console for each endpoint/method combo needing user ID. Hopefully SST devs will add it to their “constructs” to better automate or expose through CDK.

  1. Create a utility function in your serverless app like the following:
const TOKEN_STRING = "CognitoSignIn";
// cognito-idp. region.amazonaws.com/user_pool_id,cognito-idp.region.amazonaws.com/user_pool_id:CognitoSignIn:token subject claim
export function extractUserPoolId(event) {
  const cognitoAuthenticationProvider = event.headers.sub;

  /*
    For example, for an identity from an Amazon Cognito user pool, cognito-idp. 
  
      region.amazonaws.com/user_pool_id,cognito-idp.region.amazonaws.com/user_pool_id:CognitoSignIn:token subject claim

     We want the part after the CognitoSignIn   
  */
  const tokenStartIndex = cognitoAuthenticationProvider.indexOf(TOKEN_STRING);
  const subId = cognitoAuthenticationProvider.substring((tokenStartIndex + TOKEN_STRING.length + 1));

  console.log(`subId = ${subId}`);
  return subId;
}
  1. Finally, use it in your Lambdas:
const userPoolUserId = extractUserPoolId(event);

Disclaimer: as with any AWS API or structure: subject to change.

This approach is probably more suitable as AWS should not be refactoring the $context constant in the integration to retrieve this value, at least without advanced notice. I did much online investigation and found my approach here to be the best in this scenario. Feel free to use and/or update this page with the new approach. All ears if there is a better way.