How to access Cognito User Pool Arn as a function authorizer?

[SOLVED] - scroll down to last post for solution

Hi Everyone,

I’m new to serverless and have a question about accessing resources in serverless.yml. I’ve been searching/googling for a few hours abut nothing works so far.

I have a User Pool created in a resource file: resources/cognito-user-pool.yml. Snippet:

  CognitoUserPool:
    Type: AWS::Cognito::UserPool
    Properties:
...snipped...
Outputs:
  UserPoolId:
    Value:
      Ref: CognitoUserPool

Then in the serverless.yml I’m trying to access the UserPoolId (or CognitoUserPool) to construct an arn to pass to an event authorizer:

events:
  - http:
      path: <path>
      method: get
      cors: true
      authorizer:
         arn: !Join ['', ['arn:aws:cognito-idp:',!Ref AWS::Region,':',!Ref AWS::AccountId,':userpool/',!Ref CognitoUserPool]]

Now I’ve played around quite a bit with trying to get this to work. It seems that I can’t run “!Join” code on the “arn:” line of an authorizer? Can I create a custom variable?

Can someone help provide some guidance as to how I can programmatically use the generated Cognito User Pool as the authorizer?

Thanks so much for any help

Instead of attempting to construct the Arn, you can use Fn::GetAtt (or !GetAtt) to retrieve the Arn:

- http:
  ...
  authorizer:
    arn: !GetAtt 'CognitoUserPool.Arn'

or

- http:
  ...
  authorizer:
    arn: Fn::GetAtt: [CognitoUserPool, Arn]

Hope it helps.

hi @bfieber thanks for the suggestion! When I try using Fn::GetAtt: I get

  Y A M L Exception --------------------------------------
 
  incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line in "/home/val/cloudlife/git/itsacloudlife/cinekeys-project/cinekeys-test/serverless.yml" at line 98, column 28:
                  arn: Fn::GetAtt: [CognitoUserPool, Arn]
                                 ^

And when I try using !GetAtt I get the error

  Type Error ---------------------------------------------
 
  TypeError: functionArn.split is not a function
      at Object.extractAuthorizerNameFromArn (/usr/lib/node_modules/serverless/lib/plugins/aws/lib/naming.js:135:34)
      at AwsCompileApigEvents.getAuthorizer (/usr/lib/node_modules/serverless/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js:248:39)
...stack trace omitted...

Am I missing something?

Please help me to understand correctly, but, am I correct in that when you include files under resources

# you can add CloudFormation resource templates here
resources:
  - ${file(resources/api-gateway-errors.yml)}
  - ${file(resources/auroradb.yml)}
  - ${file(resources/cognito-user-pool.yml)}

that they are not run/rendered until it gets uploaded to cloudformation? If that is the case, how can the serverless.yml hope to construct the arn: line before the User Pool resource is even created?

In another attempt, I tried having the Arn saved to a SSM, and then read in the SSM, but when the function event runs, the SSM hasn’t been populated, and it errors out.

events:
      - http:
          path: <path>
          method: get
          cors: true
          authorizer:
            arn: ${ssm:/cinekeys/${self:custom.stage}/UserPoolArn, "run_again_to_get_arn"}

This code syntactically works, and if I manually populate the SSM entry, it works, but, when I “serverless remove” and “deploy”, the SSM doesn’t get generated until after it’s being referenced to. Is there some sort of “depends on” or “requires” in serverless?

Thanks again!!

SOLVED.

I was able to reference an explicitly declared authorizer. In my cognito-user-pool.yml file I added the MyApiGatewayAuthorizer section, ending up with

CognitoUserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      UserPoolName: company-${self:custom.stage}-user-pool

      UsernameAttributes:
        - email
      AutoVerifiedAttributes:
        - email

  MyApiGatewayAuthorizer: 
    Type: AWS::ApiGateway::Authorizer
    Properties: 
      AuthorizerResultTtlInSeconds: 10
      IdentitySource: method.request.header.Authorization
      Name: MyCognitoAuthorizer
      RestApiId: 
        Ref: ApiGatewayRestApi
      Type: COGNITO_USER_POOLS
      ProviderARNs: 
        - {"Fn::Join": ["", ["arn:aws:cognito-idp:", {Ref: "AWS::Region"}, ":", {Ref: "AWS::AccountId"}, ":userpool/", Ref: CognitoUserPool]]}

and in my function event definition, I specified the authorizer as such:

events:
  - http:
      path: target
      method: get
      cors: true
      authorizer:
        type: COGNITO_USER_POOLS
        authorizerId: { Ref: MyApiGatewayAuthorizer }

This worked for me. I hope it can help someone else!

6 Likes

You are god given, thank you a lot. After a day struggling around with cryptic “http parse error” or “providerArns cannot be empty” errors this is finally working.

I know this is marked as solved, but the same config doesn’t seem to apply for typescript configurations.

**Loading ..... failed with: TSError: ⨯ Unable to compile TypeScript:
  serverless.ts:102:15 - error TS2322: Type '{ type: string; authorizerId: { Ref: string; }; }' is not assignable to type 'HttpAuthorizer'.
    Object literal may only specify known properties, and 'authorizerId' does not exist in type 'HttpAuthorizer'.**

Any suggestions on how to solve for ts configurations?

I am also facing the same problem

This worked beautifully.