1
votes

I have a couple of days trying to secure my API Gateway using custom authorizers with the auth0 service. I have my lambda which validates my bearer token, the Lambda does work if I invoke it inside the AWS console and when I create a custom authorizer I can successfully tested with a Bearer token.

When I try to attach the authorizer to my API Gateway methods and test the request with postman and the token provided by auth0 it always returns a 401 status code. I read my logs in CloudWatch and the authorization Lambda it's never triggered whenever I make the HTTP request. I am following this tutorial: https://auth0.com/docs/integrations/aws-api-gateway/custom-authorizers/

And this is my Authorization lambda code:

Handler:

'use strict';

let jwtManager = require("./jwt_manager");

module.exports.authenticate = (event, context, callback) => {

  jwtManager.validate(event, function (error, data) {
    if (error) {
      if (!error) {
        context.fail("Unhandled error");
      }
      context.fail(error);
    }
    else {
      console.log("SUCCEED");
      context.succeed(data);
    }
  });

};

And this is the jwtManager:

"use strict";
require("dotenv").config({ silent: true });

let jwksClient = require("jwks-rsa");
let jwt = require("jsonwebtoken");

module.exports.validate = function(params, callback) {
  var token = validateParams(params);

  var client = jwksClient({
    cache: true,
    rateLimit: true,
    jwksRequestsPerMinute: 10,
    jwksUri: process.env.JWKS_URI
  });

  var decodedJwt = jwt.decode(token, { complete: true });
  var kid = decodedJwt.header.kid;

  client.getSigningKey(kid, function(error, data) {
    if (error) {
        console.log(error);
      callback(error);
    } else {
      var signingKey = data.publicKey || data.rsaPublicKey;
      jwt.verify(
        token,
        signingKey,
        { audience: process.env.AUDIENCE, issuer: process.env.ISSUER },
        function(error, decoded) {
            if (error) {
                console.log("ERROR");
                console.log(error);
                callback(error);
            }
            else {
                console.log(decoded);
                var response = {
                    principalId: decoded.sub,
                    policyDocument: getPolicyDocument("Allow", params.methodArn),
                    context: {
                        scope: decoded.scope
                    }
                }
                console.log(response);
                console.log(response.policyDocument);
                callback(null, response);
            }
        }
      );
    }
  });
};

function validateParams(params) {
  var token;

  if (!params.type || params.type !== "TOKEN") {
    throw new Error("Expected 'event.type' parameter to have value TOKEN");
  }

  var tokenString = params.authorizationToken;
  if (!tokenString) {
    throw new Error("Expected 'event.authorizationToken' parameter to be set");
  }

  var match = tokenString.match(/^Bearer (.*)$/);
  if (!match || match.length < 2) {
    throw new Error(
      "Invalid Authorization token - '" +
        tokenString +
        "' does not match 'Bearer .*'"
    );
  }

  return match[1];
}

 function getPolicyDocument(effect, resource) {

    var policyDocument = {};
    policyDocument.Version = '2012-10-17'; // default version
    policyDocument.Statement = [];
    var statementOne = {};
    statementOne.Action = [ 'execute-api:Invoke', 'lambda:Invoke'] ; // default action
    statementOne.Effect = effect;
    statementOne.Resource = resource.split('/')[0] + '/*';
    policyDocument.Statement[0] = statementOne;
    return policyDocument;
}

Thanks in advance!

3
Have you tried to print the policy statement and make sure they have the right Id's ? - Kannaiyan
I'm not sure that has something to do with my error, because the Lambda doesn't trigger when I do my request so API Gateway never receives the response from the Lambda - Miguel Hernández

3 Answers

1
votes

When you test an API Gateway with a custom authorizer attached but the auth lambda function never triggered, it is likely due to unsuccessful validation in token header name/ token pattern validation.

I am able to reproduce your issue.

The authorizer can only be triggered IF I change the header name from "Authorization" to "AuthorizationToken" in POSTMAN.

check the token header name I made the authorizer works

I think it is likely a new bug in AWS portal as I noticed they have changed the UI to configure API Gateway Authorizers not long ago.

It is very strange a HTTP request has to send bearer token in a header with name "AuthorizationToken". If your AWS plan allows you to access their technical support, you should alert them about this issue.

2
votes

I would like to describe how I resolved this issue.

First thing, the custom authorizer always need bearer token in authorizationToken field but from while invoking API Gateway from Postman or any other client you can send the 'Bearer Token' in authorization header, as this is an industry standard, AWS has supported it.

The trick here is in 'Token Source' while configuring the 'custom authorizer'. I have attached an image here where you can configure that 'Token Source' this field describes that the input to custom authorizer is from 'Authorization Header'. enter image description here This way, you can still send the token in 'Authorzation' header from postman, and API Gateway would copy it from 'Authorization' header and copy it to 'authorizationToken' input field while invoking custom authorizer lambda.

enter image description here Hope it's clear. Let me know if you need more details.

0
votes

In my case, the same error (lambda not triggered, authorizer failing) was due to the fact that I didn't deployed the API yet. I may be wrong, but it seems that for testing an Authorizer, your API has to be deployed at least once.

So, I deployed the API, and the authorizer test began to work.