Building a billable API by linking API Keys, Usage Plans and Cognito

I've been building a serverless REST API with AWS API Gateway, Lambda and the Serverless Framework. I want to build a website on which users sign up, choose an API plan (free, basic, pro) and get the corresponding long-lived API Key with which they can query the serverless API a certain number of times a month.

I understand that with Cognito and the self-hosted UI, you can handle user authentication easily with CUP (Cognito User Pool) Tokens, which are actually Json Web Tokens. I would like to automatically create and associate an API Key to a Usage Plan, and then to the Cognito User.

The first part is easy: with the AWS Javascript SDK, it's as simple as doing:

var params = {
  keyId: 'whateverKeyId', /* required */
  keyType: 'API_KEY', /* required */
  usagePlanId: 'whateverUsagePlanId' /* required */
};
apigateway.createUsagePlanKey(params, function(err, data) {
  if (err) console.log(err, err.stack); // an error occurred
  else     console.log(data);           // successful response
});

I'm struggling with the second part... Should I add the API Key as a custom attribute to the User Pool? But then, I don't really know how to retrieve the API Key. The documentation isn't really clear. Add the user and API Key to my own DynamoDB database after sign-up (which would be a waste of Cognito really)?

To be honest, this looks like a common scenario and the fact that I spent three days looking for the solution all over the internet without success makes me think that there's something that I don't understand...

Thanks in advance for your input!

1 answer

  • answered 2020-01-17 16:45 Xanthos Symeou

    It is a good choice to add the API key as a custom attribute in Cognito user pool.

    Every time that your user signin, cognito returns an ID token in JWT format. From this token you can retrieve cognito user's attributes both the custom attr and the default. You can find further details for cognito tokens here:

    https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html

    and

    https://aws.amazon.com/premiumsupport/knowledge-center/decode-verify-cognito-json-token/

    So what you can do:

    1. Create AWS API Gateway. Create an authorizer and connect it with you AWS cognito pool. In the Request method of each for you API resource choose this new authorizer in order to authorize the user every time that he calls your API.
    2. When your user sign in in cognito, cognito returns the ID token that i described above in JWT format, pass this JWT token as your header when you make an API call.
    3. If you do the above steps, then this JWT token will be passed to your Lambda function and inside lambda function you can retrieve the cognito custom attribute (API Key):

      const API_ID = event.requestContext.authorizer.claims['custom:apiid'];
      

    The above line, retrieves the JWT from your API authorizer header, and then retrieves the custom attribute that you created in cognito, which called "apiid" or however you want to name it.