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

Unexpected "Not Authorized" with Lambda Authorizer and Transformer v2 #10047

Closed
5 tasks done
jevakallio opened this issue Mar 22, 2022 · 13 comments
Closed
5 tasks done

Unexpected "Not Authorized" with Lambda Authorizer and Transformer v2 #10047

jevakallio opened this issue Mar 22, 2022 · 13 comments
Assignees
Labels
@auth Issues tied to @auth directive dependency-issue Issue with another dependency used graphql-transformer-v2 Issue related to GraphQL Transformer v2

Comments

@jevakallio
Copy link

jevakallio commented Mar 22, 2022

Before opening, please confirm:

  • I have installed the latest version of the Amplify CLI (see above), and confirmed that the issue still persists.
  • I have searched for duplicate or closed issues.
  • I have read the guide for submitting bug reports.
  • 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.

How did you install the Amplify CLI?

npm

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

16.13.0

Amplify CLI Version

7.6.24

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.

No

Amplify Categories

api

Amplify Commands

Not applicable

Describe the bug

N/A

Expected behavior

Note: This issue is similar to aws-amplify/amplify-category-api#100 (except we are using Lambda Authorizer instead of IAM). Based on debugging, these two issues appear to have a different root causes, so creating a new issue instead of tacking on to that one.

TL;DR;

We've recently migrated to GraphQL Transformer V2.

We are trying to disable API Key access to our API in favour or custom Lambda authorizer function, but we are receiving the following error for all queries/mutations:

Not Authorized to access {field} on type {type}

Longer version

Prior to disabling API key authentication, we were using a combination of @auth(rules: [{ allow: public }]) @aws_lambda on all our @model types, which worked. But after disabling API Key access in the project, we are not able to use allow: public due to the following error when running amplify api gql-compile or amplify push:

@auth directive with 'apiKey' provider found, but the project has no API Key authentication provider configured.

Instead, I am trying to use @auth(rules: [{ allow: custom }]) to decorate our @model types as instructed in the Custom authorization rule documentation. For example:

type User
  @model(queries: { get: "getUser" }, subscriptions: null)
  @auth(rules: [{ allow: custom }])
  @aws_lambda {
  id: ID!
  email: String!
  firstName: String
  lastName: String
}

(The documentation does not specify that we should retain the @aws_lambda AppSync decorator, but I've tried it with and without, and we get the same result)

Now, calling the following query:

{
  getUser(id:"8af1c2a0-2f0d-4e4b-b8d6-adbd04d21347") {
    id, name
  }
}

Will return:

{
  "data": {
    "getUser": null
  },
  "errors": [
    {
      "path": [
        "getUser"
      ],
      "data": null,
      "errorType": "Unauthorized",
      "errorInfo": null,
      "locations": [
        {
          "line": 2,
          "column": 3,
          "sourceName": null
        }
      ],
      "message": "Not Authorized to access getUser on type User"
    }
  ]
}

This is the API instance configuration from amplify/backend-config.json, where AppsyncAuthorization refers to a lambda deployed within the same Amplify project. These configurations seem to applied correctly (see screenshot a bit later in this issue).

{
      "service": "AppSync",
      "providerPlugin": "awscloudformation",
      "output": {
        "authConfig": {
          "defaultAuthentication": {
            "authenticationType": "AWS_LAMBDA",
            "lambdaAuthorizerConfig": {
              "lambdaFunction": "AppsyncAuthorization",
              "ttlSeconds": "0"
            }
          },
          "additionalAuthenticationProviders": []
        }
      }
    }

Digging a bit into the generated VTL resolvers for the query QuerygetUserauth0Function, I see that there's a check like this:

#if( $util.authType() == "Lambda Authorization" )
  #set( $isAuthorized = true )
#end

I'm assuming that this line is intended to bypass the default authorization requirements and pass the authorization over to the Lambda authorizer. So I logged the output of $util.authType(), and the returned value is:

"API Key Authorization"

Double checking in the AppSync console, the authorization mode is indeed set to Lambda Authorization as per the Amplify config:

image

Looking at Resolver Util Reference documentation, "Lambda Authorization" is not one of the valid return values for $util.authType().

So, what's going on here?

  • Am I using the wrong directive/syntax for specifying the @auth directive?
  • Is the resolver util $util.authType() returning an incorrect value?
  • Is the generated authorization resolver code incorrect?

And most importantly, how can I get around this problem?

Reproduction steps

See issue description.

GraphQL schema(s)

Relevant parts of schema:

type Query @aws_lambda {
	# snip...
}

type User
  @model(queries: { get: "getUser" }, subscriptions: null)
  @auth(rules: [{ allow: custom }])
  @aws_lambda {
  id: ID!
  email: String!
  firstName: String
  lastName: String
}

Additional information

@rezab777
Copy link

rezab777 commented Mar 22, 2022

What is the authorization token (authorizationToken) received by your Lambda authorizer function ?

I had an almost similar issue whereby using the cognito JWT token as authorizationToken for the Lambda authorizer would cause $util.authType() to detect it as User Pool Authorization instead of Lambda Authorization. Adding a custom string to the token solved the issue.

@jevakallio
Copy link
Author

jevakallio commented Mar 22, 2022

@rezab777 the token is already a custom string. It's either a string in the format ssm:xxxxxxxxxxxxxxxxxxxx, or a base64 encoded JSON string which contains the cognito JWT token, among other things.

Edit: Wow, you were onto something! I took another look, and ended up stumbling on this issue: aws-amplify/amplify-js#9513

Turns out that the lambda authorizer gets called correctly, but only when the token is prefixed with Bearer.

Edit #2: Ah, I spoke too soon. I tried prefixing the token with Bearer , and got a different error message "You are not authorized to make this call.", which I assumed meant the lambda was invoked correctly, but with an invalid token.

However, I updated the lambda to accept a Bearer prefixed token, and we're now back to the same error:

Not Authorized to access getProject on type Project

@sundersc
Copy link
Contributor

@jevakallio - Could you check the value of key aws_appsync_authenticationType in aws-exports.js? It looks like somehow the client request is still trying to use the API_KEY authorization.

Also, you can explicitly specify the authMode as shown below. Let me know if this helps.

const createdTodo = await API.graphql({
  query: queries.createTodo,
  variables: {input: todoDetails},
  authMode: 'AWS_LAMBDA'
});

I just tested the $util.authType() in console, it seems to be returning proper value. What is the AWS region are you using?

Thanks for mentioning about the docs, it definitely needs an update.

image

@sundersc sundersc added pending-response Issue is pending response from the issue author @auth Issues tied to @auth directive graphql-transformer-v2 Issue related to GraphQL Transformer v2 labels Mar 23, 2022
@jevakallio
Copy link
Author

jevakallio commented Mar 23, 2022

@sundersc thanks for a quick reply! What are the headers that the Amplify client library sends when specifying authMode: AWS_LAMBDA manually?

I'm not making these requests via the Amplify client -- I'm setting the Authorization HTTP header directly in GraphQL Playground and in the AppSync console editor, in both cases to the same effect (error).

However, to answer your question, the aws-exports.js file has been updated correctly, and our app is running in us-east-1.

    "aws_project_region": "us-east-1",
    "aws_appsync_graphqlEndpoint": "https://xxxxxxxxxx.appsync-api.us-east-1.amazonaws.com/graphql",
    "aws_appsync_region": "us-east-1",
    "aws_appsync_authenticationType": "AWS_LAMBDA",

To clarify:

  • Prior to this change, we had the following configuration with Transformer V1, which was working fine:
    • Default Authentication: AWS_LAMBDA (arbitrary token string provided Authorization HTTP header), configured by @aws_lambda AppSync schema directive.
    • Additional Authentication Types: API_KEY (aws-generated key provided in X-Api-Key HTTP header), configured by @aws_api_key AppSync schema directive
  • When we migrated to Transformer v2, since all fields are deny-by-default, we needed to provide @auth(rules: [{ allow: public }]) to all of our @model types. In practice, these fields were not public, since the @aws_lambda authorizer rejected unauthenticated requests.
  • However, before we go to production, we need to disable the insecure API_KEY access in our project, and rely only on AWS_LAMBDA authentication.
    • This means we can no longer use allow: public, which depends on API KEY auth
    • We're trying to use allow: custom, since we already have custom Lambda handlers in place. However, the requests are rejected by AppSync.

@sundersc
Copy link
Contributor

Using the browser developer tools, could you check whether the request headers contains the key x-api-key? AppSync infers the authorization mode based on the request headers, so it is important that the API_KEY specific headers are removed from the request.

@jevakallio
Copy link
Author

jevakallio commented Mar 23, 2022

@sundersc I can verify that the x-api-key headers are not being sent in the request. Here are all the headers that are being sent when making the request via the AppSync console.

Only authorization auth header is present in these calls. The value is an alphanumeric string.

:authority: redacted.appsync-api.us-east-1.amazonaws.com
:method: POST
:path: /graphql
:scheme: https
accept: application/json, text/plain, */*
accept-encoding: gzip, deflate, br
accept-language: en-GB,en-US;q=0.9,en;q=0.8,fi-FI;q=0.7,fi;q=0.6
authorization: redacted-token-string
content-length: 176
content-type: application/json
origin: https://us-east-1.console.aws.amazon.com
referer: https://us-east-1.console.aws.amazon.com/
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="99", "Google Chrome";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36
x-amz-user-agent: AWS-Console-AppSync/

Same is true for requests sent from the app (we use a third party graphql library, not the Amplify client).

Could you show me what the Amplify client sends when specifying the AWS_LAMBDA authentication mode?

@sundersc
Copy link
Contributor

The headers look exactly the same.
image

How did you enable Lambda authorization in V1? By default, CLI allows to configure Lambda Authorizer only for V2 api.

@sundersc sundersc self-assigned this Mar 23, 2022
@jevakallio
Copy link
Author

jevakallio commented Mar 23, 2022

@sundersc I believe the lambda authorization was configured as follows:

  1. I created the environment with API_KEY authentication by default via the Amplify CLI
  2. I manually set Lambda as the default authorization mode via the AppSync console following the Setting up AWS Lambda as authorization mode is AppSync section of this document
  3. I manually set API Key authorization as an additional auth mode, also via the console.
  4. I decorated my schema types with @aws_lambda decorators

In this setup, I was able to access the GraphQL API with an API Key (using the x-api-key header) or with Lambda authorization (using the authorization header).

Now, after I migrated to Transformer V2, I've done the following steps to move to using Lambda as the sole authorization mode in the app:

  1. Ran amplify configure api via the Amplify CLI, and configured the default auth mode to the AWS Lambda
  2. Ran amplify configure function for each of my Amplify functions (lambdas) and removed any api resource dependencies (which depended on the GraphQL API Key being output as an environment variable)
  3. Added @auth(rules: [{ allow: custom }]) to my @model types in the schema to prevent them from being denied-by-default.

@sundersc
Copy link
Contributor

I'm still unable to reproduce the issue on my end. Could you please zip the amplify folder and send to amplify-cli@amazon.com?

@jevakallio
Copy link
Author

jevakallio commented Mar 23, 2022

@sundersc, sure, I've sent it over just now.

@josefaidt josefaidt added the pending-triage Issue is pending triage label Mar 23, 2022
@sundersc
Copy link
Contributor

sundersc commented Mar 25, 2022

@jevakallio - AppSync confirmed that this is a bug and working on a fix.

To mitigate the issue, send resolverContext as empty object or any value on the authorizer lambda response. If the resolverContext is passed, then AppSync detects the authType as expected.
https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html#aws-lambda-authorization

@sundersc sundersc added dependency-issue Issue with another dependency used pending-close-response-required and removed pending-response Issue is pending response from the issue author pending-triage Issue is pending triage labels Mar 25, 2022
@jevakallio
Copy link
Author

@sundersc thank you! I can also confirm that returning resolverContext from the authorizer lambda works!

@sundersc
Copy link
Contributor

Awesome! I'm closing this issue in favor of a support ticket for AppSync team to track the issue. Feel free to reopen if required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@auth Issues tied to @auth directive dependency-issue Issue with another dependency used graphql-transformer-v2 Issue related to GraphQL Transformer v2
Projects
None yet
Development

No branches or pull requests

4 participants