Aws-mobile-appsync-sdk-js: IAM auth isn't working?

Created on 18 Jan 2018  路  24Comments  路  Source: awslabs/aws-mobile-appsync-sdk-js

I've been able to get the API key and Cognito user pool auth working without any issues. Please note that I'm using a Cognito identity pool for sign in via the aws-amplify package.

I switched the AppSync configuration (in the AWS AppSync console and in the aws-appsync client config) to IAM auth and am hitting issues.

I am going this route given that federated identities like Facebook don't seem to work with the Cognito user pool auth method since there's no jwt.

Here are the problems that I'm seeing:

1) Mutation error

Error: Can't find field listEvents on object (ROOT_QUERY) undefined.
    at readStoreResolver (http://localhost:3000/static/js/bundle.js:6315:19)
    at executeField (http://localhost:3000/static/js/bundle.js:56196:18)
    at http://localhost:3000/static/js/bundle.js:56153:31
    at Array.forEach (<anonymous>)
    at executeSelectionSet (http://localhost:3000/static/js/bundle.js:56148:29)
    at graphql (http://localhost:3000/static/js/bundle.js:56143:12)
    at diffQueryAgainstStore (http://localhost:3000/static/js/bundle.js:6347:91)
    at readQueryFromStore (http://localhost:3000/static/js/bundle.js:6290:12)
    at MyCache../node_modules/apollo-cache-inmemory/lib/inMemoryCache.js.InMemoryCache.read (http://localhost:3000/static/js/bundle.js:6033:98)
    at MyCache../node_modules/apollo-cache-inmemory/lib/inMemoryCache.js.InMemoryCache.readQuery (http://localhost:3000/static/js/bundle.js:6123:21)
    at update (http://localhost:3000/static/js/bundle.js:139224:30)
    at http://localhost:3000/static/js/bundle.js:8787:122
    at tryFunctionOrLogError (http://localhost:3000/static/js/bundle.js:10581:16)
    at http://localhost:3000/static/js/bundle.js:8787:100
    at MyCache../node_modules/apollo-cache-inmemory/lib/inMemoryCache.js.InMemoryCache.performTransaction (http://localhost:3000/static/js/bundle.js:6093:9)
    at DataStore../node_modules/apollo-client/data/store.js.DataStore.markMutationResult (http://localhost:3000/static/js/bundle.js:8786:28)
    at changeFn_1 (http://localhost:3000/static/js/bundle.js:8719:23)
    at http://localhost:3000/static/js/bundle.js:8732:21
    at MyCache../node_modules/apollo-cache-inmemory/lib/inMemoryCache.js.InMemoryCache.performTransaction (http://localhost:3000/static/js/bundle.js:6093:9)
    at http://localhost:3000/static/js/bundle.js:6105:19
    at RecordingCache../node_modules/apollo-cache-inmemory/lib/recordingCache.js.RecordingCache.record (http://localhost:3000/static/js/bundle.js:6443:9)
    at record (http://localhost:3000/static/js/bundle.js:6479:27)
    at MyCache../node_modules/apollo-cache-inmemory/lib/inMemoryCache.js.InMemoryCache.recordOptimisticTransaction (http://localhost:3000/static/js/bundle.js:6102:92)
    at DataStore../node_modules/apollo-client/data/store.js.DataStore.markMutationInit (http://localhost:3000/static/js/bundle.js:8728:24)
    at QueryManager../node_modules/apollo-client/core/QueryManager.js.QueryManager.mutate (http://localhost:3000/static/js/bundle.js:7732:24)
    at AWSAppSyncClient../node_modules/apollo-client/ApolloClient.js.ApolloClient.mutate (http://localhost:3000/static/js/bundle.js:7174:34)
    at AWSAppSyncClient../node_modules/aws-appsync/lib/client.js.AWSAppSyncClient.mutate (http://localhost:3000/static/js/bundle.js:22593:40)
    at GraphQL.dataForChildViaMutation (http://localhost:3000/static/js/bundle.js:97593:51)
    at createEvent (http://localhost:3000/static/js/bundle.js:139234:31)
    at NewEvent._callee$ (http://localhost:3000/static/js/bundle.js:138978:40)
    at tryCatch (http://localhost:3000/static/js/bundle.js:127527:40)
    at Generator.invoke [as _invoke] (http://localhost:3000/static/js/bundle.js:127761:22)
    at Generator.prototype.(anonymous function) [as next] (http://localhost:3000/static/js/bundle.js:127579:21)
    at step (http://localhost:3000/static/js/bundle.js:138920:191)
    at http://localhost:3000/static/js/bundle.js:138920:437
    at new Promise (<anonymous>)
    at http://localhost:3000/static/js/bundle.js:138920:99
    at http://localhost:3000/static/js/bundle.js:138993:30
    at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:101391:14)
    at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:101430:16)
    at Object.invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:101287:27)
    at Object.invokeGuardedCallbackAndCatchFirstError (http://localhost:3000/static/js/bundle.js:101301:43)
    at executeDispatch (http://localhost:3000/static/js/bundle.js:101685:19)
    at executeDispatchesInOrder (http://localhost:3000/static/js/bundle.js:101707:5)
    at executeDispatchesAndRelease (http://localhost:3000/static/js/bundle.js:101805:5)
    at executeDispatchesAndReleaseTopLevel (http://localhost:3000/static/js/bundle.js:101816:10)
    at Array.forEach (<anonymous>)
    at forEachAccumulated (http://localhost:3000/static/js/bundle.js:101784:9)
    at processEventQueue (http://localhost:3000/static/js/bundle.js:101961:5)
    at runEventQueueInBatch (http://localhost:3000/static/js/bundle.js:104456:3)

2) Subscription error

auth-link.js:114 Uncaught (in promise) TypeError: Cannot read property 'getPromise' of undefined
    at Object.<anonymous> (auth-link.js:114)
    at step (auth-link.js:50)
    at Object.next (auth-link.js:31)
    at auth-link.js:25
    at new Promise (<anonymous>)
    at ./node_modules/aws-appsync/lib/link/auth-link.js.__awaiter (auth-link.js:21)
    at iamBasedAuth (auth-link.js:107)
    at auth-link.js:138
    at new Subscription (zen-observable.js:103)
    at Observable.subscribe (zen-observable.js:229)
    at complex-object-link.js:74
    at <anonymous>

I've tried combing through various docs and looking at the code, however, a little uncertain about what's going sideways. Would really appreciate any guidance you can provide to try to chase it down.

question

Most helpful comment

@russelltaga Awesome! I had to several things including what you outline

  • On my client App, in the file aws-export.js I had to comment out the line 'aws_mandatory_sign_in': 'enable',. I created my cognito pools with the aws mobilehub cli and it had put that in there.
  • Like you said, I need to set the Unauthenticated role at _Federated identities > [identity pool name] > Edit identity pool_
  • Right below that, on the same page, I had to select the checkbox _Unauthenticated identities > Enable access to unauthenticated identities_
  • In the IAM Role for this cognito, under trust relationship > edit trust relationships I removed the condition "ForAnyValue:StringLike": { "cognito-identity.amazonaws.com:amr": "authenticated" } (not entirely sure if this last step was needed)
  • Set the IAM policy for the cognito role to the role provided above....

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ "arn:aws:appsync:<AWS region>:<AWS account ID>:apis/<app sync endpoint ID>/*" ] } ] }

Hopefully that helps someone in the future! Thank you, resselltaga

All 24 comments

Hi @russelltaga

Can you share how are you instantiating the client?

Also, with the latest versions of both packages (aws-appsync and aws-amplify), this should be possible:

// AppSync client instantiation
const client = new AWSAppSyncClient({
  url: AppSync.graphqlEndpoint,
  region: AppSync.region,
  auth: {
    // IAM
    type: AUTH_TYPE.AWS_IAM,
    credentials: () => Auth.currentCredentials(),
  },
});

Let me know if this helps

Aha, I lacked the credentials. Let me try that. Thank you for the amazingly fast reply!

Closing this as this is generating the signed auth header properly now. Thank you!

I'm getting 401s, however, I think I just need to do some IAM config to address that.

@russelltaga I am at the point where you were when you were getting 401s. What config did you do in IAM to resolve?

Hey @honkskillet - Add an IAM policy like this to the role that's assigned to signed in Cognito users and you should be golden.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "appsync:GraphQL"
            ],
            "Resource": [
                "arn:aws:appsync:<AWS region>:<AWS account ID>:apis/<app sync endpoint ID>/*"
            ]
        }
    ]
}

Please let me know if you run into any problems

@russelltaga awesome. Thank you. That is exact what I was looking for. Out of curiosity, do you know how you would allow unauthorized users access?

@honkskillet think all you need to do is to add the same policy to the role for unauthenticated Cognito users

@russelltaga Hmmm. When I am not signed in as a user to cognito Auth.currentCredentials() throws this error

localhost/:1 Uncaught (in promise) cannot get guest credentials when mandatory signin enabled

Where is mandatory signin enabled? Can't seem to find it when looking around the AWS web console.

If I can find it, I _think_ I can allow unauthenticated users to log in by deleting the following lines from the _trust relationship_

"ForAnyValue:StringLike": { "cognito-identity.amazonaws.com:amr": "authenticated" }

@honkskillet In the Cognito console, from what I understand the role is set via:

Federated identities > [identity pool name] > Edit identity pool

There's a dropdown for Unauthenticated role. If it's not set you may need to create one.

I don't have this enabled (so no AppSync access if you're not signed in) so it's totally possible I'm overlooking something.

@russelltaga Awesome! I had to several things including what you outline

  • On my client App, in the file aws-export.js I had to comment out the line 'aws_mandatory_sign_in': 'enable',. I created my cognito pools with the aws mobilehub cli and it had put that in there.
  • Like you said, I need to set the Unauthenticated role at _Federated identities > [identity pool name] > Edit identity pool_
  • Right below that, on the same page, I had to select the checkbox _Unauthenticated identities > Enable access to unauthenticated identities_
  • In the IAM Role for this cognito, under trust relationship > edit trust relationships I removed the condition "ForAnyValue:StringLike": { "cognito-identity.amazonaws.com:amr": "authenticated" } (not entirely sure if this last step was needed)
  • Set the IAM policy for the cognito role to the role provided above....

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ "arn:aws:appsync:<AWS region>:<AWS account ID>:apis/<app sync endpoint ID>/*" ] } ] }

Hopefully that helps someone in the future! Thank you, resselltaga

Nice @honkskillet, thanks for writing this down for everyone!

This should definitely be documented! Thanks, @honkskillet for the follow-up.

I'm having trouble getting unauthenticated access (guest credentials) to work. When I configure auth with amplify and the appsync client like this:

Amplify.configure({
  Auth: {
    identityPoolId: awsConfig.identityPoolId,
    region: awsConfig.region,
  },
});


const client = new AWSAppSyncClient({
  url: awsConfig.graphqlEndpoint,
  region: awsConfig.region,
  auth: {
    type: awsConfig.authenticationType,
    credentials: () => Auth.currentCredentials(),
  },
});

I get the following error in the console:

amplify error

Unless I enable mandatorySignIn. Then I get the localhost/:1 Uncaught (in promise) cannot get guest credentials when mandatory signin enabled error.

Finally, my appsync client works when I just use an api key, so it must be something I'm doing wrong with the cognito setup via amplify. If anyone has any tips, that would be greatly appreciated.

I'm a little uncertain based on what you described, but if the only issue is unauthenticated access, I think you may be missing some set up in Cognito

Another suggestion is to enable more verbose logging which may yield more clues:

Amplify.configure(amplifyConfig);
Amplify.Logger.LOG_LEVEL = 'DEBUG';

Let me know if that helps?

Thank you for your help @russelltaga . I do have the unauthenticated identities checkbox checked and an unauth role selected:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "mobileanalytics:PutEvents",
                "cognito-sync:*",
                "appsync:GraphQL"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

I enabled the verbose logging and there are definitely more clues. The first DEBUG line mentions a failure because of "No userPool", but I didn't think I needed a user pool for just unauthenticated access only. I thought all that was needed there was the identity pool:

{[DEBUG] 56:30.993 AuthClass - getting session failed: "No userPool"}
[DEBUG] 56:30.998 Credentials - setting credentials for guest
[DEBUG] 56:31.0 Credentials - getting credentials
[DEBUG] 56:31.1 Credentials - picking up credentials
[DEBUG] 56:31.2 Credentials - getting old cred promise
{[DEBUG] 56:31.443 Credentials - Failed to load credentials:
{[DEBUG] 56:31.450 AWSPinpointProvider - ensure credentials error: Error: Network Failure
[DEBUG] 56:31.459 AWSPinpointProvider - cannot send events without credentials, applicationId or region
[DEBUG] 56:31.460 AWSPinpointProvider - cannot send events without credentials, applicationId or region

I'm not sure if this additional info will help you offer any quick tips on perhaps a simple mistake I made. Please let me know if I can provide any more info (perhaps expanding any of the DEBUG objects above). Thanks!

I think you're right and that you do not need to configure a user pool. Based on some quick searches in the code it looks like it's generating new guest credentials, but for some reason is still failing.

https://github.com/aws-amplify/amplify-js/blob/95214df512b3062a5a37c1bd018438f32883b1ff/packages/auth/src/Auth.ts#L1012

https://github.com/aws-amplify/amplify-js/blob/f14528b30912360095e75f2117df1b3c7176904f/packages/core/src/Credentials.ts#L144

https://github.com/aws-amplify/amplify-js/blob/f14528b30912360095e75f2117df1b3c7176904f/packages/core/src/Credentials.ts#L60

https://github.com/aws-amplify/amplify-js/blob/f14528b30912360095e75f2117df1b3c7176904f/packages/core/src/Credentials.ts#L69

https://github.com/aws-amplify/amplify-js/blob/f14528b30912360095e75f2117df1b3c7176904f/packages/core/src/Credentials.ts#L251

I'm not using unauthenticated access, but will try to look more later to see if I can help you resolve it. Others have seem to have it working just fine so it likely a config thing.

One other quick idea would be to drop in some code separately to inspect what you're getting:

Auth.currentCredentials().then((creds) => {
    console.log(creds);
});

or

const creds = await Auth.currentCredentials();
console.log(creds);

Per the thread above, one thing to try is:

  • In the IAM Role for this cognito, under trust relationship > edit trust relationships I removed the condition "ForAnyValue:StringLike": { "cognito-identity.amazonaws.com:amr": "authenticated" }

If that doesn't help, try to add the cognito-identity:* permission to the unauthenticated IAM role:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "mobileanalytics:PutEvents",
                "cognito-sync:*",
                "cognito-identity:*",
                "appsync:GraphQL"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

Also ensure you've done this:

  • On my client App, in the file aws-export.js I had to comment out the line 'aws_mandatory_sign_in': 'enable'

Thanks a bunch for the ideas @russelltaga . I logged out the response to Auth.currentCredentials() and ended up getting an error with message Error: Network Failure.

I'm going to try the IAM policy and trust relationship ideas and report back.

Regarding your last suggestion, I actually didn't use Amplify CLI to generate the back-end resources so I don't have the aws-export.js file. I wonder if I should try the CLI and see if I still run into the same error? My original thought was that I should go straight to the console to create it manually rather than use the CLI since all I needed was an identity pool - but perhaps I missed a setting that the CLI otherwise would have got.

Thanks again for your tips. I'll let you know what else I find out.

I got this figured out - turns out it was a typo on my end. The region was typed in as "us_east_1" when it should have been "us-east-1". So sorry for any time wasted on your part @russelltaga . Thanks for your help.

Glad it was something easy!

My apologies for commenting on a close/old issue, but I'm facing the same exact problem. Developer authenticated identities federated through an Identity Pool cannot make AppSync calls in an Amplify project (AppSync works for separate users that are authenticated through a user pool). This is true for both Amplify's APIClass and AWSAppSyncClient.

I've made sure to change the appropriate authentication type, and as a lot of our resources are provisioned through Serverless CloudFormations, I've made sure to add an authenticated role/trust relationship with the policies attached.

By all accounts it should work, and Amplify's Auth.currentAuthenticatedUser() and Auth.currentCredentials() both resolved correctly (as far as I can tell).

Any ideas where else I should be looking?

@leosoaivan It's been a while so I'm rusty, but here's what I'd suggest:

1) Configuration

Verify the IAM role assigned to folks in the identity pool have access to your AppSync API.

In Cognito specifically, if you are allowing both authenticated and unauthenticated users, there are separate roles for each. Check the IAM policy assigned to each to verify they can access your AppSync API.

2) Crank up logging and look for clues

Amplify.configure(amplifyConfig);
Amplify.Logger.LOG_LEVEL = 'DEBUG';
const creds = await Auth.currentCredentials();
console.log(creds);

You may have already tried both. If there are any errors or other output that you can share, can try to suggest other places to look as well.

@russelltaga Thanks for the input, I was actually able to find a way to make it work this afternoon.

I can't tell if this was just a temporary work-around, but I had to add schema-level directives to the necessary type definitions in our schema.graphql, i.e. @aws_iam and @aws_cognito_user_pools. My hesitation comes from the fact that this is only briefly mentioned in the AWS AppSync docs and not in the Amplify docs.

At least now my user pool users and developer federated users can both make API calls.

Was this page helpful?
0 / 5 - 0 ratings