aws-amplify / amplify-cli

The AWS Amplify CLI is a toolchain for simplifying serverless web and mobile development.
Apache License 2.0
2.81k stars 821 forks source link

amplify function mock is using amplifyadmin #13036

Closed OperationalFallacy closed 1 year ago

OperationalFallacy commented 1 year ago

How did you install the Amplify CLI?

npm

If applicable, what version of Node.js are you using?

18.3

Amplify CLI Version

12.2.0

What operating system are you using?

Mac

Did you make any manual changes to the cloud resources managed by Amplify? Please describe the changes made.

custom function handler ("Handler": "amplify/backend/function/messageReply/lib/index.handler") AppsyncCloudWatchRole

Describe the bug

When I run amplify mock function, the graphql mutation fails with auth error

HTTP Request {
  "data": {
    "createMessage": null
  },
  "errors": [
    {
      "path": [
        "createMessage"
      ],
      "data": null,
      "errorType": "Unauthorized",
      "errorInfo": null,
      "locations": [
        {
          "line": 6,
          "column": 5,
          "sourceName": null
        }
      ],
      "message": "Not Authorized to access createMessage on type Mutation"
    }
  ]
}

Function works fine when deployed.

When I looked at the identity the mock functionality is using, by checking AWS_SECRET_ACCESS_KEY and other env vars, it was this one:

{
    "UserId": "xxx:amplifyadmin",
    "Account": "xxx",
    "Arn": "arn:aws:sts::xxx:assumed-role/us-east-1_0xhXREI6n_Full-access/amplifyadmin"
}

The user is using SSO role for interacting with AWS. The lambda is using pretty standard stuff documented in amplify and elsewhere to work with IAM auth.

const GRAPHQL_ENDPOINT = process.env['API_CHATAPI_GRAPHQLAPIENDPOINTOUTPUT']!;
const AWS_REGION = process.env['AWS_REGION'] || 'us-east-1';

export async function appSyncRequest<TResponse = any, TVariables = any>(
  query: string,
  variables: TVariables
): Promise<NonNullable<GraphQLResult<TResponse>['data']>> {
  const endpoint = new URL(GRAPHQL_ENDPOINT);

  const signer = new SignatureV4({
    credentials: defaultProvider(),
    region: AWS_REGION,
    service: 'appsync',
    sha256: Sha256
  });

  console.log('endpoint and region', GRAPHQL_ENDPOINT, signer)

  const requestToBeSigned = new HttpRequest({
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      host: endpoint.host
    },
    hostname: endpoint.host,
    body: JSON.stringify({query, variables}),
    path: endpoint.pathname
  });

  const signed = await signer.sign(requestToBeSigned);
  console.log('Signed request', signed)
  const request = new Request(endpoint, signed);

  const response = await fetch(request);
  const body = await response.json();

  if (body.errors) {
    console.log('HTTP Request', JSON.stringify(body,null,2))
    throw body.errors;
  }

  return body.data;
}

Expected behavior

Function mock should work with admin type of SSO roles.

The docs should point out how 'amplify mock' is utilizing SSO roles, it's not completely clear. The amplify mock api fails

amplify mock api                               
Failed to start API Mocking. Running cleanup tasks.
Reason: start has timed out!

I also tried amplify aws reset-cache, no bueno.

Reproduction steps

I'm not sure how to reproduce it, because I don't understand the state which amplify is, specifically what IAM roles its using for which actions.

Project Identifier

2d635f4ba4d64d1865c244f526648caa

Log output

``` # Put your logs below this line there is nothing in the logs, amplify function mock xxx ```

Additional information

No response

Before submitting, please confirm:

OperationalFallacy commented 1 year ago

I managed to reconfigure the project to use the SSO role with Admin policy and verified that temp keys belong to the SSO user. The requests still unauthorized. I guess the graphql allows IAM access only for the roles Lambda is assigned.

custom-roles.json doesn't seem to have any effect on how the mock is working, it always using SSO credentials configured for the project.

{
  "adminRoleNames": ["amplify-chatapi-dev-22028-authRole"]
}

Why is the mock using this role when I've added custom-roles.json? 2023-07-31T17:47:57.220Z|info : amplify-provider-awscloudformation.system-config-manager.getProfileConfig(["sso-dev-amplify1"])

OperationalFallacy commented 1 year ago

There is also a discussion about using fake key id ASIAVJKIAM-AuthRole, which doesn't apply to the example amplify docs provide

https://docs.amplify.aws/guides/functions/graphql-from-lambda/q/platform/ios/#iam-authorization

  const mockCredentials = { 
      "accessKeyId": "ASIAVJKIAM-AuthRole", 
      "secretAccessKey": "fake"
  }

  const credentials = process.env['AWS_EXECUTION_ENV']?.endsWith("mock") ? mockCredentials : defaultProvider();
  console.log('credentials', credentials)

  const signer = new SignatureV4({
    credentials: credentials,
    region: AWS_REGION,
    service: 'appsync',
    sha256: Sha256
  });

Error

HTTP Request {
  "errors": [
    {
      "errorType": "UnrecognizedClientException",
      "message": "The security token included in the request is invalid."
    }
  ]

Well, this is quite a rabbit hole. I hope somebody could help with mocking, without mock functionality the feedback time for development is disastrous.

OperationalFallacy commented 1 year ago

Ok, figured it out. I didn't realize custom-roles.json has server-side effects. I thought its something local. As soon as changes applied with amplify push, mocks are working.

github-actions[bot] commented 1 year ago

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.

neuquen commented 9 months ago

@OperationalFallacy I'm seeing the same exact error. Did you ever get this working using the mock credentials?

OperationalFallacy commented 9 months ago

@OperationalFallacy I'm seeing the same exact error. Did you ever get this working using the mock credentials?

Yes it's working as designed

I've added the role assigned by sso to 'custom-roles.json'

Amplify push

And function mock started to work.

If function needs access to amplify resources, GraphQL for example, it will need user's IAM role in custom-roles.json deployed, so appsync can handle access.

Usually the access to AWS from local machine works with AWS profile, with sso access or other types of credentials. No extra configuration required.

graphql (appsync) needs extra information about roles to configure access

neuquen commented 9 months ago

Thanks for the response @OperationalFallacy. I was able to get my setup working using the custom-roles.json file as well.

If function needs access to amplify resources, GraphQL for example, it will need user's IAM role in custom-roles.json deployed, so appsync can handle access.

I did the same as you and added individual IAM users in the file if a particular developer needed to mock it locally.

However, I also saw the same fake id ASIAVJKIAM-AuthRole and mock credentials as an alternate solution. I would prefer to use the fake ID and mock credentials so that I don't have to keep updating the custom-roles.json file every time a new developer needs to mock that function locally.

Were you able to find a solution that used the fake ID and mock credentials?

OperationalFallacy commented 9 months ago

AWS SSO is the anwer, you should assign developers a group in your corp directory and then login with a specific role then

Something like this

AWSReservedSSO_Developer_someId/email and AWSReservedSSO_Developer_someId goes into the config.

neuquen commented 8 months ago

One other question here @OperationalFallacy related to this statement:

Usually the access to AWS from local machine works with AWS profile, with sso access or other types of credentials. No extra configuration required.

How did you configure your mock function to use a different profile? Right now it uses the default profile and I cannot seem to use a custom profile which uses the SSO role.

OperationalFallacy commented 8 months ago

@neuquen

add custom-roles.json to amplify/backend/api/yourapi

{
  "notes": "These are the SSO roles the users use when working with aws accounts to run mock functions. Amplify allows IAM authorization on graphql for the Lambda with a special roles created for lambda only. Hence to mock function run locally, we must add our SSO user roles here",
  "adminRoleNames": ["AWSReservedSSO_Administrator_xxx", "AWSReservedSSO_Administrator_xxx"],
  "roleEnvMap": ["dev", "stage"]
}

You can find what role you have when login into aws account, top right corner.