awslabs / aws-mobile-appsync-sdk-js

JavaScript library files for Offline, Sync, Sigv4. includes support for React Native
Apache License 2.0
921 stars 266 forks source link

Running appsync in nodejs using AWS_IAM, it uses my aws-cli credentials #300

Open philiiiiiipp opened 5 years ago

philiiiiiipp commented 5 years ago

Do you want to request a feature or report a bug? bug?

What is the current behavior? Running a query against appsync using AWS_IAM authorisation on a computer where you have the aws-cli installed and the default profile is set. It uses those as the credentials instead of the current logged in user.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.

Run a query against a appsync endpoint while having the default aws-cli parameters set.

const client = new AWSAppSyncClient({
  disableOffline: true,
  url: AmplifyConfig.aws_appsync_graphqlEndpoint,
  region: AmplifyConfig.aws_appsync_region,
  auth: {
    type: 'AWS_IAM',
    credentials: () => Amplify.Auth.currentCredentials(),
  },
});

Auth.signIn(USER.username, USER.password)
  .then(() => client.hydrated())
  .then(client =>
    client.query({
      query: GetUser,
      variables: {},
      fetchPolicy: 'network-only',
    }),
  )
  .then(result => {
    console.log(result);
  });

This will execute the query with the currently installed permissions and user rather than the logged in one.

This seems like it could make quite some damage and or screw up tests.

What is the expected behavior? I would expect it to use the currently logged in credentials ( which it does once I remove the default credentials from my aws config )

Which versions and which environment (browser, react-native, nodejs) / OS are affected by this issue? Did this work in previous versions? nodejs

philiiiiiipp commented 5 years ago

It also seems to happen if the environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are set.

manueliglesias commented 5 years ago

Hi @philiiiiiipp

I haven't been able to replicate the issue, I always get back the cognito identity, this is what I tried (note that I tested this while having the aws cli env variables set pointing to valid credentials):

Do you have any more info that could help me replicate?

require('es6-promise').polyfill();
require('isomorphic-fetch');

const { default: Amplify, default: { Auth } } = require('aws-amplify');
const { AWSAppSyncClient } = require('aws-appsync');
const AmplifyConfig = require('./aws-config');
const gql = require('graphql-tag');

Amplify.configure(AmplifyConfig);
// Amplify.Logger.LOG_LEVEL = 'DEBUG';

const client = new AWSAppSyncClient({
    disableOffline: true,
    url: AmplifyConfig.aws_appsync_graphqlEndpoint,
    region: AmplifyConfig.aws_appsync_region,
    auth: {
        type: 'AWS_IAM',
        credentials: () => Amplify.Auth.currentCredentials(),
        // credentials: { accessKeyId: process.env.AWS_ACCESS_KEY, secretAccessKey: process.env.AWS_SECRET_KEY }
    },
});

(async () => {
    try {
        const user = await Auth.signIn(process.env.USER, process.env.PASSWORD);

        // console.log(user.signInUserSession.accessToken.jwtToken);

        const result = await client.query({
            query: gql`query Q {
                whoAmI
            }`,
            variables: {},
            fetchPolicy: 'network-only',
        });

        const { data: { whoAmI } } = result;

        console.log(JSON.stringify(JSON.parse(whoAmI), null, 2));
    } catch (error) {
        console.error(error);
    } finally {
        process.exit();
    }
})();
{
  "accountId": "999999999999",
  "cognitoIdentityPoolId": "us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "cognitoIdentityId": "us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "sourceIp": [
    "XX.XX.XX.XX"
  ],
  "username": "AROAJQQIILWZK24H3GKFE:CognitoIdentityCredentials",
  "userArn": "arn:aws:sts::999999999999:assumed-role/TT0161882385-AppsyncTestAuthenticatedRole-XXXXXXXXXXXXX/CognitoIdentityCredentials",
  "cognitoIdentityAuthType": "authenticated",
  "cognitoIdentityAuthProvider": "\"cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxxxxxxx\",\"cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxxxxxxx:CognitoSignIn:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\""
}

My GraphQL query:

type Query {
    whoAmI: String
}

I am using a None data source for this query, with this request mapping template:

#**
Resolvers with None data sources can locally publish events that fire
subscriptions or otherwise transform data without hitting a backend data source.
The value of 'payload' is forwarded to $ctx.result in the response mapping template.
*#
{
    "version": "2017-02-28",
    "payload": "$util.escapeJavaScript($util.toJson($context.identity))"
}
philiiiiiipp commented 5 years ago

Hmm interesting. That looks pretty much how I do it. Maybe it's the version I am using.

OS: MacOS 10.14.1
npm: 6.4.1
node: v8.12.0
aws: aws-cli/1.15.33 Python/2.7.10 Darwin/18.2.0 botocore/1.10.33

$  npm list aws-sdk
@dh/sfa-api@0.0.99 /Users/philipp/node/dev/dh/sfa-api
├─┬ @dathuis/serverless-dynamodb-local@0.2.39
│ └── aws-sdk@2.363.0  deduped
├─┬ aws-amplify@1.1.10
│ └─┬ @aws-amplify/core@1.0.18
│   └── aws-sdk@2.329.0 
├─┬ aws-appsync@1.5.1
│ └── aws-sdk@2.329.0 
├── aws-sdk@2.363.0 
└─┬ serverless@1.33.2
  └── aws-sdk@2.363.0

Apart from that, I am running it within jest.

manueliglesias commented 5 years ago

Can you try adding a top level dependency and locking the aws-sdk version to aws-sdk@2.329.0 ?

philiiiiiipp commented 5 years ago

I did not have time to test this specific use case, but I am seing other problems when having multiple versions of the aws-sdk installed.

E.g. the storage module does not seem to work with

[WARN] 01:18.711 AWSS3Provider - error uploading { CredentialsError: Missing credentials in config
        at ClientRequest.<anonymous> (/Users/philipp/node/dev/dh/sfa-api/node_modules/aws-sdk/lib/http/node.js:83:34)
        at Object.onceWrapper (events.js:313:30)
        at emitNone (events.js:106:13)
        at ClientRequest.emit (events.js:208:7)
        at Socket.emitTimeout (_http_client.js:707:34)
        at Object.onceWrapper (events.js:313:30)
        at emitNone (events.js:106:13)
        at Socket.emit (events.js:208:7)
        at Socket._onTimeout (net.js:422:8)
        at ontimeout (timers.js:498:11)
        at tryOnTimeout (timers.js:323:5)
        at Timer.listOnTimeout (timers.js:290:5)
      message: 'Missing credentials in config',
      code: 'CredentialsError',
      time: 2018-12-18T23:01:18.706Z,
      retryable: true,
      originalError: 
       { message: 'Could not load credentials from any providers',
         code: 'CredentialsError',
         time: 2018-12-18T23:01:18.706Z,
         retryable: true,
         originalError: 
          { message: 'Connection timed out after 1000ms',
            code: 'TimeoutError',
            time: 2018-12-18T23:01:18.706Z,
            retryable: true,
            status: null },
         status: null } }

Manually removing the other aws-sdk's from the package-lock.json makes this work.

I would assume that the original issue is caused by the same. I am wondering on how to go about this, adding multiple versions of the aws-sdk in a project is done quite quickly looking at all the dev plugins etc. and the only way I can see this being locked down is by maually editing the package-lock.json which is very cumbersome if you also want to make regular updates.

Is there no way that the amplify packages could somehow forcefully hand down their aws-sdk or the current config?

philiiiiiipp commented 5 years ago

Which is not only a problem with the mobile-appsync-sdk but also seems to affect the Amplify library as I can see it. Could also be that I am doing something incredibly stupid.

philiiiiiipp commented 5 years ago

I have now tried to lock it, to no avail. As long as I have a [default] set in my credentials file it will use those over the logged in user.

.then(() => {
        const { email, password } = testEnvironment.testApi.getLoginAdminUser();
        return Auth.signIn(email, password);
      })
      .then(() => Auth.currentCredentials())
      .then(cred => {
        console.log(cred);
      })

Gives me with [default] set:

  console.log src/appsync/__integration_test__/Setup.js:128
    SharedIniFileCredentials {
      expired: false,
      expireTime: null,
      accessKeyId: 'XXXXXX'
      sessionToken: undefined,
      refreshCallbacks: [],
      filename: undefined,
      profile: 'default',
      disableAssumeRole: true,
      preferStaticCredentials: false,
      tokenCodeFn: null }

and removing [default]

 CognitoIdentityCredentials {
      expired: false,
      expireTime: 2019-02-04T15:22:40.000Z,
      accessKeyId: 'XXXXXXXX',
      sessionToken: 'AgoGb3JpZ2luEOr//////////wEaCWV1LXdlc3QtMSKAAj3bmO+mukcLtoyTxVi6MPPUVu7Me857eodKinaCmzhmnJD6uyuk3WbTXs+fIvvYLmIzqQHMNDXR69wN7jZ/Oo0uAlLxWvGuU34EvOH/qqxqN97ernx7zMWP1LUC5A6gferctOUfXYtVB0LCjLXXXXXXX',
      params: 
       { IdentityPoolId: 'eu-west-1:XXXXXXXX,
         Logins: 
...

I tried it with and without aws-sdk set to 2.329

$npm list aws-sdk
@dh/sfa-api@0.0.99 /Users/philipp/node/dev/dh/sfa-api
├─┬ @aws-amplify/core@1.0.22
│ └── aws-sdk@2.329.0 
├─┬ aws-appsync@1.7.1
│ └── aws-sdk@2.329.0 
├── aws-sdk@2.329.0 

It did not make any difference.