Access to cloud formation output of a cognito user pool in authorizer

i am a serverless noob, so i don’t think this is a bug per say, but more of me not understanding how things work. i have searched through the issues and docs and have not come up with a way to make this work correctly. i am having a sort of “which came first, the chicken or the egg” problem.

issue at hand

to see issue, just run the following command

sls deploy

you will get an error with this message

Serverless Error ---------------------------------------

  Stack with id serverless-cf-reference-issue-dev does not exist

if you comment out / remove the following section under the function events, and then run sls deploy it will work. you can than add back the following section and re-run sls deploy and it will work!

authorizer:
  arn: ${cf:${self:service}-${self:provider.stage}.UserPoolArn}
  scopes: - email

so how can i reference this cognito pool for authorization without running into this issue? am i doing something fundamentally wrong?

serverless.yml

service: serverless-cf-reference-issue

provider:
  name: aws
  runtime: nodejs12.x
  region: us-east-2

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
          cors: true
          authorizer:
            arn: ${cf:${self:service}-${self:provider.stage}.UserPoolArn}
            scopes:
              - email

resources:
  Resources:
    CognitoUserPool:
      Type: AWS::Cognito::UserPool
      Properties:
        UserPoolName: "serverless-cf-reference-issue"
        UsernameAttributes:
          - email
        AdminCreateUserConfig:
          AllowAdminCreateUserOnly: true
        Policies:
          PasswordPolicy:
            MinimumLength: 10
            RequireLowercase: True
            RequireNumbers: True
            RequireSymbols: False
            RequireUppercase: True
            TemporaryPasswordValidityDays: 2
    CognitoUserPoolClient:
      Type: AWS::Cognito::UserPoolClient
      Properties:
        ClientName: user-pool-client-app
        GenerateSecret: False
        AllowedOAuthFlows:
          - code
          - implicit
        AllowedOAuthScopes:
          - email
          - openid
        CallbackURLs:
          - http://localhost:9000
        SupportedIdentityProviders:
          - COGNITO
        AllowedOAuthFlowsUserPoolClient: true
        UserPoolId:
          Ref: CognitoUserPool
  Outputs:
    UserPoolArn:
      Description: The ARN for the cognito user pool
      Value: !GetAtt CognitoUserPool.Arn

you can see the code @ https://github.com/tommyk/serverless-cf-reference-issue

Hi! If I’m understanding the issue correctly, then this is a tricky one that I ran into a while back. I posted here about the solution I found at the time and I’ll attempt to adapt it to your example here. Hopefully this still works in the latest version of serverless.

The configuration will end up looking something like this (notice the change to the endpoint authorizer, and the new ApiGatewayAuthorizer resource.

service: serverless-cf-reference-issue

provider:
  name: aws
  runtime: nodejs12.x
  region: us-east-2

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
          cors: true
          authorizer:
            type: COGNITO_USER_POOLS
            authorizerId:
              Ref: ApiGatewayAuthorizer

resources:
  Resources:
    CognitoUserPool:
      Type: AWS::Cognito::UserPool
      Properties:
        UserPoolName: "serverless-cf-reference-issue"
        UsernameAttributes:
          - email
        AdminCreateUserConfig:
          AllowAdminCreateUserOnly: true
        Policies:
          PasswordPolicy:
            MinimumLength: 10
            RequireLowercase: True
            RequireNumbers: True
            RequireSymbols: False
            RequireUppercase: True
            TemporaryPasswordValidityDays: 2
    CognitoUserPoolClient:
      Type: AWS::Cognito::UserPoolClient
      Properties:
        ClientName: user-pool-client-app
        GenerateSecret: False
        AllowedOAuthFlows:
          - code
          - implicit
        AllowedOAuthScopes:
          - email
          - openid
        CallbackURLs:
          - http://localhost:9000
        SupportedIdentityProviders:
          - COGNITO
        AllowedOAuthFlowsUserPoolClient: true
        UserPoolId:
          Ref: CognitoUserPool
ApiGatewayAuthorizer:
      DependsOn:
        - ApiGatewayRestApi
      Type: AWS::ApiGateway::Authorizer
      Properties:
        Name: whatever
        IdentitySource: method.request.header.Authorization
        RestApiId:
          Ref: ApiGatewayRestApi
        Type: COGNITO_USER_POOLS
        ProviderARNs:
          - Fn::GetAtt: [CognitoUserPool, Arn]

P.S. (self promotion ahead, hopefully that’s okay here)
The complexity I experienced in getting Cognito set up and working is the main reason I created JustAuthenticateMe - I even made a serverless framework plugin to make the backend portion of endpoint authentication ultra simple: https://github.com/CoalesceSoftware/serverless-justauthenticateme-plugin