Amplify-js: Getting 403 error on API.get() Federated Identities with aws_iam authorizer - Angular 6

Created on 23 Jun 2018  路  12Comments  路  Source: aws-amplify/amplify-js

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

What is the current behavior?
I'm using Federated Identity pool only, no User Pool. I've enabled unauthenticated identities in order to make API calls required before user authentication. All of that is working correctly. However for all other API calls I've enabled aws-iam authorizer. When I call API.get() on the aws_iam enabled routes I get a 403, but all public API.get() calls are working correctly. What's weird is that if I enter the same credentials on Postman using AWS Signature type Authentication the request works, which is why I think it's a aws-amplify issue.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than AWS Amplify.
I'm using Manual setup for Amplify, here is an example of my current config:

Amplify.configure({
    Auth: {
        identityPoolId: 'REGION:POOL_ID',
        region: 'us-east-2',
        mandatorySignIn: false
    },
    API: {
        endpoints: [
            {
                name: environment.API_NAME,
                endpoint: environment.API_ENDPOINT
            }
        ]
    }
});

This is what my request looks like:

const path = `/users`;
    const init = {
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      }
    };
   API.get(this._apiName, path, init)
    .then(res => console.log(res))
    .catch(error => console.error(error));

I've enabled CORS on my lambda function, and like I said it works on Postman, just a detail to keep in mind. Here is the error I receive on Chrome console:

Failed to load https://API-GATEWAY-URL.execute-api.us-east-2.amazonaws.com/dev/users: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. The response had HTTP status code 403.
search.component.ts:21 Error: Network Error
    at createError (createError.js:16)
    at XMLHttpRequest.handleError [as __zone_symbol__ON_PROPERTYerror] (xhr.js:87)
    at XMLHttpRequest.wrapFn (zone.js:1188)
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421)
    at Object.onInvokeTask (core.js:4053)
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:420)
    at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:188)
    at ZoneTask.push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask [as invoke] (zone.js:496)
    at invokeTask (zone.js:1540)
    at XMLHttpRequest.globalZoneAwareCallback (zone.js:1566)
(anonymous) @ search.component.ts:21
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:388
onInvoke @ core.js:4062
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:387
push../node_modules/zone.js/dist/zone.js.Zone.run @ zone.js:138
(anonymous) @ zone.js:872
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:421
onInvokeTask @ core.js:4053
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:420
push../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:188
drainMicroTaskQueue @ zone.js:595
push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask @ zone.js:500
invokeTask @ zone.js:1540
globalZoneAwareCallback @ zone.js:1566
error (async)
customScheduleGlobal @ zone.js:1666
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:407
onScheduleTask @ zone.js:297
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:401
push../node_modules/zone.js/dist/zone.js.Zone.scheduleTask @ zone.js:232
push../node_modules/zone.js/dist/zone.js.Zone.scheduleEventTask @ zone.js:258
(anonymous) @ zone.js:1831
desc.set @ zone.js:1244
dispatchXhrRequest @ xhr.js:84
ZoneAwarePromise @ zone.js:891
xhrAdapter @ xhr.js:12
dispatchRequest @ dispatchRequest.js:59
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:388
onInvoke @ core.js:4062
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:387
push../node_modules/zone.js/dist/zone.js.Zone.run @ zone.js:138
(anonymous) @ zone.js:872
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:421
onInvokeTask @ core.js:4053
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:420
push../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:188
drainMicroTaskQueue @ zone.js:595
push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask @ zone.js:500
invokeTask @ zone.js:1540
globalZoneAwareCallback @ zone.js:1566
XMLHttpRequest.send (async)
scheduleTask @ zone.js:2969
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:407
push../node_modules/zone.js/dist/zone.js.Zone.scheduleTask @ zone.js:232
push../node_modules/zone.js/dist/zone.js.Zone.scheduleMacroTask @ zone.js:255
scheduleMacroTaskWithCurrentZone @ zone.js:1114
(anonymous) @ zone.js:3001
proto.(anonymous function) @ zone.js:1394
handleRequest @ xhr.js:81
executeSend @ event_listeners.js:304
SEND @ event_listeners.js:318
callListeners @ sequential_executor.js:101
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
(anonymous) @ state_machine.js:26
(anonymous) @ request.js:38
(anonymous) @ request.js:685
callListeners @ sequential_executor.js:115
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
(anonymous) @ state_machine.js:26
(anonymous) @ request.js:38
(anonymous) @ request.js:685
callListeners @ sequential_executor.js:115
callNextListener @ sequential_executor.js:95
(anonymous) @ event_listeners.js:148
(anonymous) @ util.js:725
hash @ util.js:458
sha256 @ util.js:409
computeSha256 @ util.js:723
COMPUTE_SHA256 @ event_listeners.js:142
callListeners @ sequential_executor.js:101
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
(anonymous) @ state_machine.js:26
(anonymous) @ request.js:38
(anonymous) @ request.js:685
callListeners @ sequential_executor.js:115
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
(anonymous) @ state_machine.js:26
(anonymous) @ request.js:38
(anonymous) @ request.js:685
callListeners @ sequential_executor.js:115
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
runTo @ request.js:403
send @ request.js:367
makeUnauthenticatedRequest @ service.js:218
getCredentialsForIdentity @ cognitoidentity.js:13
getCredentialsForIdentity @ cognito_identity_credentials.js:250
(anonymous) @ cognito_identity_credentials.js:167
(anonymous) @ cognito_identity_credentials.js:225
(anonymous) @ request.js:364
callListeners @ sequential_executor.js:105
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
(anonymous) @ state_machine.js:26
(anonymous) @ request.js:38
(anonymous) @ request.js:685
callListeners @ sequential_executor.js:115
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
(anonymous) @ state_machine.js:26
(anonymous) @ request.js:38
(anonymous) @ request.js:685
callListeners @ sequential_executor.js:115
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
(anonymous) @ state_machine.js:26
(anonymous) @ request.js:38
(anonymous) @ request.js:685
callListeners @ sequential_executor.js:115
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
(anonymous) @ state_machine.js:26
(anonymous) @ request.js:38
(anonymous) @ request.js:685
callListeners @ sequential_executor.js:115
callNextListener @ sequential_executor.js:95
onEnd @ event_listeners.js:269
push../node_modules/events/events.js.EventEmitter.emit @ events.js:78
finishRequest @ xhr.js:124
(anonymous) @ xhr.js:39
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:421
push../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:188
push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask @ zone.js:496
invokeTask @ zone.js:1540
globalZoneAwareCallback @ zone.js:1566
XMLHttpRequest.send (async)
scheduleTask @ zone.js:2969
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:407
push../node_modules/zone.js/dist/zone.js.Zone.scheduleTask @ zone.js:232
push../node_modules/zone.js/dist/zone.js.Zone.scheduleMacroTask @ zone.js:255
scheduleMacroTaskWithCurrentZone @ zone.js:1114
(anonymous) @ zone.js:3001
proto.(anonymous function) @ zone.js:1394
handleRequest @ xhr.js:81
executeSend @ event_listeners.js:304
SEND @ event_listeners.js:318
callListeners @ sequential_executor.js:101
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
(anonymous) @ state_machine.js:26
(anonymous) @ request.js:38
(anonymous) @ request.js:685
callListeners @ sequential_executor.js:115
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
(anonymous) @ state_machine.js:26
(anonymous) @ request.js:38
(anonymous) @ request.js:685
callListeners @ sequential_executor.js:115
callNextListener @ sequential_executor.js:95
(anonymous) @ event_listeners.js:148
(anonymous) @ util.js:725
hash @ util.js:458
sha256 @ util.js:409
computeSha256 @ util.js:723
COMPUTE_SHA256 @ event_listeners.js:142
callListeners @ sequential_executor.js:101
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
(anonymous) @ state_machine.js:26
(anonymous) @ request.js:38
(anonymous) @ request.js:685
callListeners @ sequential_executor.js:115
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
(anonymous) @ state_machine.js:26
(anonymous) @ request.js:38
(anonymous) @ request.js:685
callListeners @ sequential_executor.js:115
emit @ sequential_executor.js:77
emit @ request.js:683
transition @ request.js:22
runTo @ state_machine.js:14
runTo @ request.js:403
send @ request.js:367
makeUnauthenticatedRequest @ service.js:218
getId @ cognitoidentity.js:9
getId @ cognito_identity_credentials.js:222
refresh @ cognito_identity_credentials.js:164
get @ credentials.js:121
(anonymous) @ util.js:797
ZoneAwarePromise @ zone.js:891
promise @ util.js:796
(anonymous) @ Auth.js:1478
ZoneAwarePromise @ zone.js:891
push../node_modules/aws-amplify/lib/Auth/Auth.js.AuthClass._loadCredentials @ Auth.js:1477
(anonymous) @ Auth.js:1468
step @ Auth.js:52
(anonymous) @ Auth.js:33
fulfilled @ Auth.js:24
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:388
push../node_modules/zone.js/dist/zone.js.Zone.run @ zone.js:138
(anonymous) @ zone.js:872
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:421
push../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:188
drainMicroTaskQueue @ zone.js:595
Promise.then (async)
scheduleMicroTask @ zone.js:578
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:410
push../node_modules/zone.js/dist/zone.js.Zone.scheduleTask @ zone.js:232
push../node_modules/zone.js/dist/zone.js.Zone.scheduleMicroTask @ zone.js:252
scheduleResolveOrReject @ zone.js:862
ZoneAwarePromise.then @ zone.js:962
step @ Auth.js:26
(anonymous) @ Auth.js:27
ZoneAwarePromise @ zone.js:891
push../node_modules/aws-amplify/lib/Auth/Auth.js.__awaiter @ Auth.js:23
push../node_modules/aws-amplify/lib/Auth/Auth.js.AuthClass._setCredentialsFromFederation @ Auth.js:1431
push../node_modules/aws-amplify/lib/Auth/Auth.js.AuthClass._refreshFederatedToken @ Auth.js:1023
push../node_modules/aws-amplify/lib/Auth/Auth.js.AuthClass.currentUserCredentials @ Auth.js:974
push../node_modules/aws-amplify/lib/Auth/Auth.js.AuthClass.keepAlive @ Auth.js:1532
push../node_modules/aws-amplify/lib/Auth/Auth.js.AuthClass.pickupCredentials @ Auth.js:1368
push../node_modules/aws-amplify/lib/Auth/Auth.js.AuthClass.currentCredentials @ Auth.js:1028
push../node_modules/aws-amplify/lib/Analytics/Analytics.js.AnalyticsClass._getCredentials @ Analytics.js:280
(anonymous) @ Analytics.js:152
step @ Analytics.js:44
(anonymous) @ Analytics.js:25
(anonymous) @ Analytics.js:19
ZoneAwarePromise @ zone.js:891
push../node_modules/aws-amplify/lib/Analytics/Analytics.js.__awaiter @ Analytics.js:15
push../node_modules/aws-amplify/lib/Analytics/Analytics.js.AnalyticsClass.startSession @ Analytics.js:148
analyticsEvent @ index.js:95
push../node_modules/aws-amplify/lib/Analytics/index.js.Analytics.onHubCapsule @ index.js:52
(anonymous) @ Hub.js:62
push../node_modules/aws-amplify/lib/Common/Hub.js.HubClass.toListeners @ Hub.js:60
push../node_modules/aws-amplify/lib/Common/Hub.js.HubClass.dispatch @ Hub.js:35
authEvent @ index.js:84
push../node_modules/aws-amplify/lib/Analytics/index.js.Analytics.onHubCapsule @ index.js:46
(anonymous) @ Hub.js:62
push../node_modules/aws-amplify/lib/Common/Hub.js.HubClass.toListeners @ Hub.js:60
push../node_modules/aws-amplify/lib/Common/Hub.js.HubClass.dispatch @ Hub.js:35
dispatchAuthEvent @ Auth.js:66
push../node_modules/aws-amplify/lib/Auth/Auth.js.AuthClass.configure @ Auth.js:193
(anonymous) @ Amplify.js:19
push../node_modules/aws-amplify/lib/Common/Amplify.js.Amplify.configure @ Amplify.js:18
./src/main.ts @ main.ts:10
__webpack_require__ @ bootstrap:76
0 @ main.ts:31
__webpack_require__ @ bootstrap:76
checkDeferredModules @ bootstrap:43
webpackJsonpCallback @ bootstrap:30
(anonymous) @ main.js:1
Cross-Origin Read Blocking (CORB) blocked cross-origin response https://API-GATEWAY-URL.execute-api.us-east-2.amazonaws.com/dev/users with MIME type application/json. See https://www.chromestatus.com/feature/SOME-ID for more details.

What is the expected behavior?
For the API.get() request to succeed and return the list of users, as it does in Postman.

Which versions of Amplify, and which browser / OS are affected by this issue? Did this work in previous versions?
"aws-amplify": "^0.4.1",
"aws-amplify-angular": "^0.1.4",
MacOS High Sierra 10.13.5
Google Chrome Version 67.0.3396.87 (up to date)

I have not checked previous versions, but will do so if you think it would help.

You can turn on the debug mode to provide more info for us by setting window.LOG_LEVEL = 'DEBUG'; in your app.

[DEBUG] 43:41.447 Signer - GET
/dev/users

accept:application/json
content-type:application/json
host:API-GATEWAY-URL.execute-api.us-east-2.amazonaws.com
x-amz-date:20180623T134341Z
x-amz-security-token:TOKEN==

accept;content-type;host;x-amz-date;x-amz-security-token
SECURITY-TOKEN

One thing I noticed with the log level = debug is that the signer region is different than the one I set in the Amplify config, they don't match, could that be the issue? It's in both the signer and the Authorization, in the Credential key

[DEBUG] 43:41.448 Signer:
  region:"us-east-1" 
  service:"execute-api"
"[DEBUG] 43:41.449 RestClient - Signed Request: ":
data:null
headers:
  Accept:"application/json"
  Authorization:"AWS4-HMAC-SHA256 Credential=SOME-TOKEN/20180623/us-east-1/execute-api/aws4_request, SignedHeaders=accept;content-type;host;x-amz-date;x-amz-security-token, Signature=SIGNATURE-TOKEN"
  Content-Type:"application/json"
  X-Amz-Security-Token:"SECRET-TOKEN=="
  x-amz-date:"20180623T134341Z"

host:"API-GATEWAY-URL.execute-api.us-east-2.amazonaws.com"
method:"GET"
path:"/dev/users"
url:"https://API-GATEWAY-URL.execute-api.us-east-2.amazonaws.com/dev/users"

Thank you for your time and help 馃憤

API question

Most helpful comment

You can add region parameter to your endpoint add that will fix the problem.

Amplify.configure({
    Auth: {
        identityPoolId: 'REGION:POOL_ID',
        region: 'us-east-2',
        mandatorySignIn: false
    },
    API: {
        endpoints: [
            {
                name: environment.API_NAME,
                endpoint: environment.API_ENDPOINT
                region: 'api region'
            }
        ]
    }
});

All 12 comments

@Cris87

Thanks for reaching out!

When you say that you enabled CORs on your lambda, you mean that you enabled it via API Gateway?

I ask becaue it sounds like you could very well be having a CORs issue outside of Amplify, since Postman typically doesn't care about CORs and will allow requests to succeed in cases where browsers would not.

@haverchuck

Thanks for getting back to me so quickly 馃憤

Yes, you are correct I did mean the API Gateway :). I'm using Serverless framework and my backend is a small Express.js app, I've installed cors npm and initialize it using: app.use(cors()); as per npm instructions. When that did not work I manually enabled it directly in API Gateway as well but got the same result.

@elorzafe hello, please let me know if you require any more information. I'm currently building a solution for a client and really need this to work. Thank you.

@Cris87 when I use serverless on AWS Lambda and Amazon API Gateway I am doing something like this.

```var express = require('express')
var bodyParser = require('body-parser')
var AWS = require('aws-sdk')
var awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')

// declare a new express app
var app = express()
app.use(bodyParser.json())
app.use(awsServerlessExpressMiddleware.eventContext())

// Enable CORS for all methods
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*")
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
next()
});

AWS.config.update({ region: process.env.REGION })

/******

  • Example get method *
    ******/

app.get('/mypath', function(req, res) {
// Add your code here
// Return the API Gateway event and query string parameters for example
res.json(req.apiGateway.event);
});
```

API Gateway returns 403 when lambda doesn't return a proper response, you can take a look on AWS CloudWatch (logs) to see if your function is getting called. You can find that on AWS Lambda console, go to your function, then click on Monitoring tab and click on Jump to Logs link.

@elorzafe thanks so much for getting back to me. I tried using your example code for enabling cors but now all endpoints return 403, event the ones that were working before.

This is what my express app.js file init looks like:

const serverless = require('serverless-http');
const bodyParser = require('body-parser');
const express = require('express');
const cors = require('cors');
const app = express();

app.use(bodyParser.json({ strict: false }));
app.use(cors());

/* Routes */
const users = require('./routes/users');

app.get('/', (req, res) => {
  res.send('Welcome');
});

app.use('/users', users);

module.exports.handler = serverless(app);

I've reverted back to using cors they way I had it as the other endpoints are working correctly.

Also just in case it helps, this is my serverless.yml

# serverless.yml
service: my-app--serverless-api--dev

provider:
  name: aws
  runtime: nodejs6.10
  region: us-east-2
  iamRoleStatements:
    - Effect: "Allow"
      Action: 
        - "rds:*"
        - "execute-api:*"
      Resource: "*"

plugins:
  - serverless-offline

functions:
  app:
    handler: index.handler
    events:
      - http: ANY /
      - http: 'ANY {proxy+}'
  getUsers:
    handler: index.handler
    events:
      - http:
          path: users
          method: GET
          authorizer: aws_iam
          cors:
            origin: '*'
            allowCredentials: false
            headers:
              - Content-Type
              - Authorization
              - X-Amz-Date
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
  getUser:
    handler: index.handler
    events:
      - http: 'GET /users/{proxy+}'
  createUser:
    handler: index.handler
    events:
      - http: 'POST /users'

However I really don't think it's related to serverless, or the aws set up because the call only fails when I make it using aws-amplify.

I still think it may be related to the region being set to us-east-1 in the Authorization header, in the Credential Signature by amplify but my code is in us-east-2 as indicated in the Amplify config. Because when the Authorization is not required the calls work correctly, but when they are required to be verified it does not work.

Thanks

You can add region parameter to your endpoint add that will fix the problem.

Amplify.configure({
    Auth: {
        identityPoolId: 'REGION:POOL_ID',
        region: 'us-east-2',
        mandatorySignIn: false
    },
    API: {
        endpoints: [
            {
                name: environment.API_NAME,
                endpoint: environment.API_ENDPOINT
                region: 'api region'
            }
        ]
    }
});

Hi there,

I was having a similar issue so hopefully my answer will help.

You are having CORS errors on the console because the APIGW is not authenticating and thereafter not getting to the point that you add the headers to allow it. So probably not related with your actual issue.

403 is usually authentication related, so what @elorzafe said will most likely fix your problem.

@elorzafe that fixed it!!! YAY!! Thank you so much!! 馃憤

馃槃

I have tried @elorzafe suggestion but still getting 403! any other suggestions?

I was also getting a 403 error after creating different stages(dev, staging, prod). With a similar CORS error message in the console as the OP. After spending a bit of time investigating this supposedly CORS error I realised it was because in my Amplify Auth config I was still pointing to the dev user pool and federated identity in my staging and prod configs and this is because in API gateway I have as Authorization: AWS_IAM, see serverless.yaml config:

functions:
  list:
    handler: list.main
    events:
      - http:
          path: registries
          method: get
          cors: true
          authorizer: aws_iam

This shows how most of the time when you get a CORS related error, the issue usually has nothing to do with CORS itself

[Serverless users] I lost a complete day trying to debug the same issue and reading the excellent serverless-stack tutorial I found THE problem.

As @caiocezart says, a 403 error is related to API Gateway and no to CORS.

Solution:
Don't forget config how your serverless app handles gateway error.

You can follow this guide to fix the 403 error (then CORS issue)

https://serverless-stack.com/chapters/handle-api-gateway-cors-errors.html

Hope helps you guys!

Was this page helpful?
0 / 5 - 0 ratings