Repository: serverless/serverless (JavaScript)
Date: 2017-09-02 15:48:12+00:00
Task type: Bug Fix

Issue:
Cannot reference an authorizer already created within services that shares the same API GW
<!--
1. If you have a question and not a bug/feature request please ask it at http://forum.serverless.com
2. Please check if an issue already exists so there are no duplicates
3. Check out and follow our Guidelines: https://github.com/serverless/serverless/blob/master/CONTRIBUTING.md
4. Fill out the whole template so we have a good overview on the issue
5. Do not remove any section of the template. If something is not applicable leave it empty but leave it in the Issue
6. Please follow the template, otherwise we'll have to ask you to update it
-->

# This is a (Bug Report)

## Description

* What went wrong?
I have two services (e.g. Service-A and Service-B) that shares the same API Gateway. Service-A has some public/private endpoints and defines an API GW authorizer. This works fine.
Service-B has some private endpoints that need to use the authorizer defined in Service-A. I tried to reference the authorizer by ARN with no success. I think the problem is that serverless tries to create another authorizer in the same API GW, and throws error because an authorizer with the same name already exists. The thing is that I dont want to create another API GW authorizer, I just want to reference an authorizer that belongs to the API GW.
Output relevant to Service-A
```json
{
    "AuthorizerApiGatewayAuthorizer":{
        "Type":"AWS::ApiGateway::Authorizer",
        "Properties":{
            "AuthorizerResultTtlInSeconds":0,
            "IdentitySource":"method.request.header.Authorization",
            "Name":"authorizer",
            "RestApiId":"XXXXXXXX",
            "AuthorizerUri":{
                "Fn::Join":[
                    "",
                    [
                        "arn:aws:apigateway:",
                        {
                            "Ref":"AWS::Region"
                        },
                        ":lambda:path/2015-03-31/functions/",
                        {
                            "Fn::GetAtt":[
                                "AuthorizerLambdaFunction",
                                "Arn"
                            ]
                        },
                        "/invocations"
                    ]
                ]
            },
            "Type":"TOKEN"
        }
    }
}
```
Output relevant to Service-B
```json
{
    "AuthorizerApiGatewayAuthorizer":{
        "Type":"AWS::ApiGateway::Authorizer",
        "Properties":{
            "IdentitySource":"method.request.header.Authorization",
            "Name":"authorizer",
            "RestApiId":"XXXXXXXX",
            "AuthorizerUri":{
                "Fn::Join":[
                    "",
                    [
                        "arn:aws:apigateway:",
                        {
                            "Ref":"AWS::Region"
                        },
                        ":lambda:path/2015-03-31/functions/",
                        "MY_LAMBDA_FUNCTION_AUTHORIZER_ARN",
                        "/invocations"
                    ]
                ]
            },
            "Type":"TOKEN"
        }
    }
}
```
* What did you expect should have happened?
Having Service-A and Service-B within the same API GW, I expect to be able to reference the authorizer already defined by Service-A.
* What was the config you used?
I have tried this alternatives in Service-B
```yml
functions:
 some-function:
   handler: someHandler.someFunction
   events:
     - http:
         path: some/path
         method: get
         authorizer:
           arn: LAMBDA_ARN
           name: service-a-authorizer-name
```
```yml
functions:
 some-function:
   handler: someHandler.someFunction
   events:
     - http:
         path: some/path
         method: get
         authorizer: LAMBDA_ARN
```
* What stacktrace or error message from your provider did you see?
An error occurred: AuthorizerApiGatewayAuthorizer - Authorizer name must be unique. Authorizer authorizer already exists in this RestApi..

## Additional Data

* ***Serverless Framework Version you're using***: 1.26
* ***Operating System***: Linux (kernel 4.13.0-32-generic)
* ***Stack Trace***:
* ***Provider Error messages***: 



Issue discussion:

Solution patch:
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/authorization.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/authorization.js
index 62e0acfa61d..d295b11efff 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/authorization.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/authorization.js
@@ -13,6 +13,15 @@ module.exports = {
     }
 
     if (http.authorizer) {
+      if (http.authorizer.type && http.authorizer.authorizerId) {
+        return {
+          Properties: {
+            AuthorizationType: http.authorizer.type,
+            AuthorizerId: http.authorizer.authorizerId,
+          },
+        };
+      }
+
       const authorizerLogicalId = this.provider.naming
         .getAuthorizerLogicalId(http.authorizer.name);
 
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js
index 1d7e55b9d34..36a27282241 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js
@@ -227,6 +227,7 @@ module.exports = {
     let resultTtlInSeconds;
     let identityValidationExpression;
     let claims;
+    let authorizerId;
 
     if (typeof authorizer === 'string') {
       if (authorizer.toUpperCase() === 'AWS_IAM') {
@@ -239,7 +240,10 @@ module.exports = {
         name = this.provider.naming.extractAuthorizerNameFromArn(arn);
       }
     } else if (typeof authorizer === 'object') {
-      if (authorizer.type && authorizer.type.toUpperCase() === 'AWS_IAM') {
+      if (authorizer.type && authorizer.authorizerId) {
+        type = authorizer.type;
+        authorizerId = authorizer.authorizerId;
+      } else if (authorizer.type && authorizer.type.toUpperCase() === 'AWS_IAM') {
         type = 'AWS_IAM';
       } else if (authorizer.arn) {
         arn = authorizer.arn;
@@ -284,6 +288,7 @@ module.exports = {
       type,
       name,
       arn,
+      authorizerId,
       resultTtlInSeconds,
       identitySource,
       identityValidationExpression,


Test patch:
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js
index c81ed7edb3a..e24dc8719c0 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js
@@ -314,6 +314,32 @@ describe('#compileMethods()', () => {
     });
   });
 
+  it('should set custom authorizer config with authorizeId', () => {
+    awsCompileApigEvents.validated.events = [
+      {
+        functionName: 'First',
+        http: {
+          path: 'users/create',
+          method: 'post',
+          authorizer: {
+            type: 'COGNITO_USER_POOLS',
+            authorizerId: 'gy7lyj',
+          },
+        },
+      },
+    ];
+    return awsCompileApigEvents.compileMethods().then(() => {
+      expect(
+        awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
+          .Resources.ApiGatewayMethodUsersCreatePost.Properties.AuthorizationType
+      ).to.equal('COGNITO_USER_POOLS');
+      expect(
+        awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
+          .Resources.ApiGatewayMethodUsersCreatePost.Properties.AuthorizerId
+      ).to.equal('gy7lyj');
+    });
+  });
+
   it('should set authorizer config if given as ARN string', () => {
     awsCompileApigEvents.validated.events = [
       {
@@ -322,6 +348,7 @@ describe('#compileMethods()', () => {
           authorizer: {
             name: 'Authorizer',
           },
+          integration: 'AWS',
           path: 'users/create',
           method: 'post',
         },