Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

{ errorType: 'UnauthorizedException', message: 'Permission denied' } when calling mutation from GitHub action #2457

Open
2 tasks done
duckbytes opened this issue Apr 15, 2024 · 4 comments
Labels
pending-response question Further information is requested transferred

Comments

@duckbytes
Copy link

How did you install the Amplify CLI?

No response

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

16

Amplify CLI Version

12.10.1

What operating system are you using?

linux

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

No manual changes made

Describe the bug

I have a mutation query in my schema:

registerTenant(name: String, emailAddress: String, tenantName: String): Tenant @function(name: "plateletAddNewTenant-${env}") @auth(rules: [{allow: private, provider: iam}])

that I want to be able to call from a GitHub action. So far I've been able to set up permissions that allow the IAM account to call that mutation, but I'm getting this error:

{ errorType: 'UnauthorizedException', message: 'Permission denied' }

This is the policy I'm using for AppSync:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "appsync:GraphQL"
            ],
            "Resource": [
                "arn:aws:appsync:*:<id>:apis/*/types/Query/fields/getTenantByTenantName",
                "arn:aws:appsync:*:<id>:apis/*/types/Mutation/resolvers/registerTenant"
            ]
        }
    ]
}

It seems like it can call the getTenantByTenantName query fine, but gets that error on the registerTenant call.

in amplify/backend/api/platelet/custom-roles.json I have the following (with my proper id):

{
    "adminRoleNames": [
        "arn:aws:iam::<id>:user/test-test.bloodbikes.cloud"
    ]
}

I use this on another API I have and get no issue when making calls to it using IAM.

I tested running the action with a full admin access policy attached to the IAM user but it was still denied, so it seems like the API is returning that permission denied error rather than it being a problem with the policy.

It's strange that no error is returned when using getTenantByTenantName, which is just a queryField added to an index, but the registerTenant which is a custom function throws that error.

type Tenant
@auth(rules: [
{allow: private, operations: [read]},
{allow: private, provider: iam, operations: [read]},
])
@model {
  id: ID!
  name: String! @index(name: "byTenantName", queryField: "getTenantByTenantName")
  referenceIdentifier: String!
  admin: User! @hasOne
}

Expected behavior

I expect the query to complete without a permission denied error.

Reproduction steps

  1. Add a custom mutation to type Mutation with lambda function.
  2. Create access keys and set IAM policy to allow running that mutation.
  3. Set up GitHub action that calls the mutation.

This is the code I use in the GitHub action:

import * as core from "@actions/core";
import { Sha256 } from "@aws-crypto/sha256-js";
import { SignatureV4 } from "@aws-sdk/signature-v4";
import { HttpRequest } from "@aws-sdk/protocol-http";
import fetch, { Request } from "node-fetch";
import {
  RegisterTenantMutationVariables,
  GetTenantByTenantNameQueryVariables,
} from "./API";
import { registerTenant } from "./graphql/mutations";
import { getTenantByTenantName } from "./graphql/queries";

const request = async (queryDetails: { variables: any; query: string }) => {
  const apiURL = core.getInput("tenantApiUrl");
  const region = core.getInput("awsRegion");
  const accessKeyId = core.getInput("awsAccessKeyId");
  const secretAccessKey = core.getInput("awsSecretAccessKey");
  console.log("apiURL", apiURL);
  console.log("region", region);
  console.log("queryDetails", queryDetails);

  const endpoint = new URL(apiURL);
  const credentials = {
    accessKeyId,
    secretAccessKey,
  };
  const signer = new SignatureV4({
    credentials,
    region,
    service: "appsync",
    sha256: Sha256,
  });

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

  const signed = await signer.sign(requestToBeSigned);
  const request = new Request(endpoint, signed);
  return await fetch(request);
};

const errorCheck = (body: any) => {
  if (body?.errors) {
    console.error(body?.errors);
    throw new Error(body?.errors[0].message);
  }
};

export const registerTenantQuery = async (
  input: RegisterTenantMutationVariables
) => {
  const response = await request({
    variables: input,
    query: registerTenant,
  });
  const body = await response.json();
  errorCheck(body);
  return body?.data?.registerTenant;
};

export const getTenantByName = async (
  variables: GetTenantByTenantNameQueryVariables
) => {
  const response = await request({
    variables,
    query: getTenantByTenantName,
  });
  const body = await response.json();
  errorCheck(body);

  console.log("aaaa: ", body?.data?.getTenantByTenantName?.items);
  return body?.data?.getTenantByTenantName?.items[0] || null;
};

Project Identifier

f97b80f7ee5ceef2a72f107f35e6d453

Log output

# Put your logs below this line


Additional information

No response

Before submitting, please confirm:

  • I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.
  • I have removed any sensitive information from my code snippets and submission.
@ykethan
Copy link

ykethan commented Apr 15, 2024

Hey👋 thanks for raising this! I'm going to transfer this over to our API repository for better assistance 🙂

@ykethan ykethan transferred this issue from aws-amplify/amplify-cli Apr 15, 2024
@biller-aivy
Copy link

Why are you defining a separate policy when you define the Auth rules with the Auth directive @auth in the mutation declaration?

@duckbytes
Copy link
Author

duckbytes commented Apr 18, 2024

@biller-aivy the policy is for my IAM user that I use with GitHub actions. I want to be able to call that mutation from that user.

If I don't add any policy, I get a different kind of unauthorized error.

@palpatim
Copy link
Member

palpatim commented May 9, 2024

Hi @duckbytes,

I see you're declaring your IAM policy resource with resolvers in the registerTenant clause:

"arn:aws:appsync:*:<id>:apis/*/types/Mutation/resolvers/registerTenant"

I believe that should be fields:

"arn:aws:appsync:*:<id>:apis/*/types/Mutation/fields/registerTenant"

Please see AWS AppSync documentation for supported resource types.

Hope this helps.

@palpatim palpatim added question Further information is requested pending-response and removed pending-triage labels May 9, 2024
@palpatim palpatim removed their assignment May 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pending-response question Further information is requested transferred
Projects
None yet
Development

No branches or pull requests

4 participants