Connect to API Gateway with IAM Auth

From @jayair on Tue Oct 31 2017 23:46:03 GMT+0000 (UTC)

@dailenspencer This is element-app-client correct?

I logged in an everything seems fine. I’m using NPM but it seems curious that your package.json does not have the aws-sdk like the one we have - https://github.com/AnomalyInnovations/serverless-stack-demo-client/blob/master/package.json#L7

Any idea why it’s not in there?

From @dailenspencer on Wed Nov 01 2017 17:07:04 GMT+0000 (UTC)

@jayair Apologies. The correct folder is isomorphic/

From @jayair on Wed Nov 01 2017 17:46:12 GMT+0000 (UTC)

@dailenspencer I’m not sure how much I can help you with your Isomorphic app. This isn’t a project based on create-react-app so my experience is a little limited here. But I noticed the aws-sdk is not in your package.json in this project either. Any idea why?

From @fcostarodrigo on Tue Nov 21 2017 17:11:50 GMT+0000 (UTC)

@dailenspencer I am having the same issue as you.

Basically when sigV4Config.secretKey is undefined, sigV4Client.newClient(sigV4Config) returns and empty object {} that doesn’t have the signRequest function, causing the error you see.

This happens when we call authUser twice. While the first call waits for the promise in getAwsCredentials to resolve, the second call overwrites the config in AWS.config.credentials = new AWS.CognitoIdentityCredentials. Then, when the promise of the first call resolves, the config won’t be the original config with the secret key you were waiting for.
This bug doesn’t happen always, if you keep refreshing the page, you can see that sometimes the code does work.

This is my workaround:

let loadingCredentials = null;

function getAwsCredentials(userToken) {
  if (loadingCredentials) return loadingCredentials;

  const authenticator = `cognito-idp.${config.cognito
    .REGION}.amazonaws.com/${config.cognito.USER_POOL_ID}`;

  AWS.config.update({ region: config.cognito.REGION });

  AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: config.cognito.IDENTITY_POOL_ID,
    Logins: {
      [authenticator]: userToken
    }
  });

  loadingCredentials = AWS.config.credentials.getPromise();

  await loadingCredentials;

  loadingCredentials = null;
}

From @jayair on Wed Nov 22 2017 18:45:53 GMT+0000 (UTC)

@fcostarodrigo Thanks for tracking this down This one has been a tough one to reproduce. I’m going to take a better look and figure it out.

From @jayair on Wed Nov 29 2017 18:14:00 GMT+0000 (UTC)

@fcostarodrigo I’ve been trying to reproduce this and I haven’t been able to. Are you just refreshing the home page constantly trying to get the error? Also, if you could share your repo and the browser you are using, that would help greatly.

From @fcostarodrigo on Wed Nov 29 2017 19:09:19 GMT+0000 (UTC)

@jayair I tried to write a test to reproduce the error, but it is really hard. It depends on the order the promises are resolved.

I will create a new project in my personal account and share it with you soon. Maybe our changes introduced the bug too, who knows.

Thanks.

From @fcostarodrigo on Sun Dec 03 2017 10:00:45 GMT+0000 (UTC)

@jayair
I reproduced the bug here: https://github.com/fcostarodrigo/notes-app-client
I had to make a few changes to the original code to trigger the bug.

  1. In Home.js you have to check if the user is logged with await authUser() instead of this.props.isAuthenticated.
  2. In App.js you have to render even when this.state.isAuthenticating is true.

If App.js is the only thing checking if the user is authenticated and all other components wait for this check to come from App.js, then things work fine. But if another component tries to see if the user is authenticated concurrently, then the code can fail.

I guess this is not really a bug, it just happens that authUser should not be called concurrently.

From @jayair on Mon Dec 04 2017 00:29:55 GMT+0000 (UTC)

@fcostarodrigo Thanks for putting this together.

Yeah I see what you mean about calling authUser concurrently. I’m not sure how with the original code it can happen.

From @fcostarodrigo on Mon Dec 04 2017 11:53:02 GMT+0000 (UTC)

@jayair Yes, this won’t happen in the original code. It only happens if you modify it enough.

From @appernetic on Tue Jan 09 2018 19:23:14 GMT+0000 (UTC)

A similar error also happens in the original code. I have followed the guide to this step and just got:

TypeError: WEBPACK_IMPORTED_MODULE_0_aws_sdk_global.util.crypto.lib.randomBytes is not a function

From @jayair on Sat Jan 13 2018 02:33:39 GMT+0000 (UTC)

@appernetic Can you share your repo? I need to try this out. It really should not happen.

From @xpresslanej on Sun Jan 14 2018 14:16:24 GMT+0000 (UTC)

@appernetic - see this thread: https://github.com/aws/amazon-cognito-identity-js/issues/646

From @qwtel on Wed Jan 31 2018 10:06:32 GMT+0000 (UTC)

For anybody who’d rather not depend on a JS crypto implementation, I’ve created a modified version of sigV4Client.js that uses the Web Cryptography API. It only uses crypto-js as a fallback (Browser support looks pretty good though).

Since web crypto uses promises, signRequest now needs to return a promise as well.
You’ll have to add an extra await when using it:

const signedRequest = await sigV4Client // added `await`
  .newClient({...})
  .signRequest({...})

Unrelated: This tutorial series is amazing. Thanks so much for making this available!

From @sanbeaman on Wed Jan 31 2018 17:42:25 GMT+0000 (UTC)

Great info on this topic!
I modified your project for what I thought would be a simpler task, but I’m having a fetch / CORs issue.

So I know the following issue is NOT an issue with your code, but I’m hoping someone can point me in the right direction. Because I’ve made the following changes:

  1. DynamoDb has a single primary key, no sort key
  2. I’m still using Congnito & Federated Identities, but not using the userid as part of the sort key in dynamodb. Basically, I just want an authorized user to be able to view / modify any item in the database. (ISsue probably related to this, maybe I sholdn’t use FEderated Ids…?)
  3. I removed the file upload to s3 portion (I made sure to keep the api gateway policy info when I removed the s3 part)

ISSUE: When I login, I’m placed on the home page and a list of items is fetched and displayed. Great!. However, clicking on one of the items. I’ll get:

TypeError: Failed to Fetch

Failed to load https://xxxxx.execute-api.us-east-1.amazonaws.com/prod/contestents/+13135551212: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:3000’ is therefore not allowed access. The response had HTTP status code 403. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

API works using CLI and your test cli tool,
have same issue if client is running locally or published from s3…

Why would the scan call work on Home.js (I changed the query call to a scan call since I no longer have a sort key) but subsequent GET calls get denied access?
I’m thinking it must be on client side and possibly with sigV4Client.js… but I’ve been troubleshooting without success for awhile now…
thank you!

From @jayair on Wed Jan 31 2018 23:41:36 GMT+0000 (UTC)

@sanbeaman If you are having some trouble debugging this and you think you’ve looked at all the usual suspects, I’d suggest trying to look through the logs and seeing what is going on - https://serverless-stack.com/chapters/api-gateway-and-lambda-logs.html.

From @sanbeaman on Thu Feb 01 2018 21:33:36 GMT+0000 (UTC)

@jayair thanks for the response. I’ve since removed the serverless API’s and have started to research IAM , Cognito, etc… in more detail. The guides helped me thru a number of things, and I hope to revist the cloudformation and s3 upload info at a later date! thank you