question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How correctly setup AWS::ApiGatewayV2::Authorizer?

See original GitHub issue

Hello I have some issue to correctly setup the AWS::ApiGatewayV2::Authorizer for my API.

According to the documentation I create this:

Resources:
  DevWebSocket:
    Type: 'AWS::ApiGatewayV2::Api'
    Properties:
      Name: TL-Dev-WebSocket-API
      ProtocolType: WEBSOCKET
      RouteSelectionExpression: $request.body.action
  DevAuthorizerLambda:
    Type: 'AWS::Serverless::Function'
    Properties:
      CodeUri: WebSockets/Authorizer
      Role: 'arn:aws:iam::************:role/LambdaDynamoDB'
      Environment:
        Variables:
          STAGE: Dev
  DevAuthorizerLambdaPermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: 'lambda:invokeFunction'
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: DevAuthorizerLambda
      SourceArn:
        'Fn::Sub':
          - >-
            arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/$connect
          - __Stage__: '*'
            __ApiId__:
              Ref: DevWebSocket
  DevWebSocketAuthorizer:
    Type: 'AWS::ApiGatewayV2::Authorizer'
    Properties:
      Name: DevAuthorizer
      ApiId:
        Ref: DevWebSocket
      AuthorizerType: REQUEST
      AuthorizerUri:
        'Fn::Sub': >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DevAuthorizerLambda.Arn}/invocations
      IdentitySource:
        - route.request.querystring.token
  DevWebSocketDeployment:
    Type: 'AWS::ApiGatewayV2::Deployment'
    Properties:
      ApiId:
        Ref: DevWebSocket
    DependsOn:
      - WebSocketPart1 # ref routes to avoid error 'need a least one route to create this'
  DevWebSocketStage:
    Type: 'AWS::ApiGatewayV2::Stage'
    Properties:
      StageName: Dev
      Description: Dev
      DeploymentId:
        Ref: DevWebSocketDeployment
      ApiId:
        Ref: DevWebSocket
  [...]

But currently I get all the time Unauthorized and my lambda for authorization is not trigger at all (no cloudwacth logs).

Thank you in advance for your help.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:3
  • Comments:6

github_iconTop GitHub Comments

2reactions
Hideman85commented, Jul 9, 2019

Yes, I completely forget this issue but I solved it a moment ago with helps of AWS support.

Template.yaml :

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Dev-APIs
Globals:
  Function:
    Timeout: 10
    Handler: index.handler
    Runtime: nodejs8.10
Resources:
  DevWebSocket:
    Type: 'AWS::ApiGatewayV2::Api'
    Properties:
      Name: TL-Dev-WebSocket-API
      ProtocolType: WEBSOCKET
      RouteSelectionExpression: $request.body.action
  DevAuthorizerLambda:
    Type: 'AWS::Serverless::Function'
    Properties:
      CodeUri: WebSockets/Authorizer
      Role: 'arn:aws:iam::${AWS::AccountId}:role/AWSLambdaBasicExecutionRole '
      Environment:
        Variables:
          STAGE: Dev
  DevAuthorizerLambdaPermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: 'lambda:InvokeFunction'
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: DevAuthorizerLambda
      SourceArn:
        'Fn::Sub':
          - >-
            arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/$connect
          - __Stage__: '*'
            __ApiId__:
              Ref: DevWebSocket
  DevWebSocketAuthorizer:
    Type: 'AWS::ApiGatewayV2::Authorizer'
    Properties:
      Name: DevAuthorizer
      ApiId:
        Ref: DevWebSocket
      AuthorizerType: REQUEST
      AuthorizerUri:
        'Fn::Sub': >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DevAuthorizerLambda.Arn}/invocations
      AuthorizerCredentialsArn: 'arn:aws:iam::${AWS::AccountId}:role/APIGatewayLambdaInvokeRole'
      IdentitySource:
        - route.request.querystring.token
  DevWebSocketDeployment:
    Type: 'AWS::ApiGatewayV2::Deployment'
    Properties:
      ApiId:
        Ref: DevWebSocket
    DependsOn:
      - WebSocketPart1
  DevWebSocketStage:
    Type: 'AWS::ApiGatewayV2::Stage'
    Properties:
      StageName: Dev
      Description: Dev
      DeploymentId:
        Ref: DevWebSocketDeployment
      ApiId:
        Ref: DevWebSocket
  WebSocketPart1:
    Type: 'AWS::Serverless::Application'
    Properties:
      Location: WebSocketEntries.yaml
      Parameters:
        DevWebSocket:
          Ref: DevWebSocket
        DevWebSocketAuthorizer:
          Ref: DevWebSocketAuthorizer

WebSocketEntries.yaml :

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Dev-TopLegal-WebSocket-API-Functions-Part1
Globals:
  Function:
    Timeout: 10
    Handler: index.handler
    Runtime: nodejs8.10
Parameters:
  DevWebSocket:
    Type: String
  DevWebSocketAuthorizer:
    Type: String
Resources:
  DevWebSocketonConnect:
    Type: 'AWS::Serverless::Function'
    Properties:
      CodeUri: WebSockets/onConnect
      Role: 'arn:aws:iam::${AWS::AccountId}:role/AWSLambdaBasicExecutionRole'
      Environment:
        Variables:
          STAGE: Dev
  DevonConnectPermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: 'lambda:InvokeFunction'
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: DevWebSocketonConnect
      SourceArn:
        'Fn::Sub':
          - >-
            arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/${__Name__}
          - __Stage__: '*'
            __ApiId__:
              Ref: DevWebSocket
            __Name__: $connect
  DevonConnectIntegration:
    Type: 'AWS::ApiGatewayV2::Integration'
    Properties:
      ApiId:
        Ref: DevWebSocket
      Description: DevonConnectIntegration
      IntegrationType: AWS_PROXY
      IntegrationUri:
        'Fn::Sub': >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DevWebSocketonConnect.Arn}/invocations
      IntegrationMethod: POST
  DevonConnectRoute:
    Type: 'AWS::ApiGatewayV2::Route'
    Properties:
      ApiId:
        Ref: DevWebSocket
      RouteKey: $connect
      OperationName: DevonConnectRoute
      Target:
        'Fn::Join':
          - /
          - - integrations
            - Ref: DevonConnectIntegration
      AuthorizationType: CUSTOM
      AuthorizerId:
        Ref: DevWebSocketAuthorizer
    DependsOn: DevonConnectIntegration
  DevWebSocketonDefault:
    Type: 'AWS::Serverless::Function'
    Properties:
      CodeUri: WebSockets/onDefault
      Role: 'arn:aws:iam::${AWS::AccountId}:role/AWSLambdaBasicExecutionRole'
      Environment:
        Variables:
          STAGE: Dev
  DevonDefaultPermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: 'lambda:InvokeFunction'
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: DevWebSocketonDefault
      SourceArn:
        'Fn::Sub':
          - >-
            arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/${__Name__}
          - __Stage__: '*'
            __ApiId__:
              Ref: DevWebSocket
            __Name__: $default
  DevonDefaultIntegration:
    Type: 'AWS::ApiGatewayV2::Integration'
    Properties:
      ApiId:
        Ref: DevWebSocket
      Description: DevonDefaultIntegration
      IntegrationType: AWS_PROXY
      IntegrationUri:
        'Fn::Sub': >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DevWebSocketonDefault.Arn}/invocations
      IntegrationMethod: POST
  DevonDefaultRoute:
    Type: 'AWS::ApiGatewayV2::Route'
    Properties:
      ApiId:
        Ref: DevWebSocket
      RouteKey: $default
      OperationName: DevonDefaultRoute
      Target:
        'Fn::Join':
          - /
          - - integrations
            - Ref: DevonDefaultIntegration
    DependsOn: DevonDefaultIntegration
  DevWebSocketonDisconnect:
    Type: 'AWS::Serverless::Function'
    Properties:
      CodeUri: WebSockets/onDisconnect
      Role: 'arn:aws:iam::${AWS::AccountId}:role/AWSLambdaBasicExecutionRole'
      Environment:
        Variables:
          STAGE: Dev
  DevonDisconnectPermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: 'lambda:InvokeFunction'
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: DevWebSocketonDisconnect
      SourceArn:
        'Fn::Sub':
          - >-
            arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/${__Name__}
          - __Stage__: '*'
            __ApiId__:
              Ref: DevWebSocket
            __Name__: $disconnect
  DevonDisconnectIntegration:
    Type: 'AWS::ApiGatewayV2::Integration'
    Properties:
      ApiId:
        Ref: DevWebSocket
      Description: DevonDisconnectIntegration
      IntegrationType: AWS_PROXY
      IntegrationUri:
        'Fn::Sub': >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DevWebSocketonDisconnect.Arn}/invocations
      IntegrationMethod: POST
  DevonDisconnectRoute:
    Type: 'AWS::ApiGatewayV2::Route'
    Properties:
      ApiId:
        Ref: DevWebSocket
      RouteKey: $disconnect
      OperationName: DevonDisconnectRoute
      Target:
        'Fn::Join':
          - /
          - - integrations
            - Ref: DevonDisconnectIntegration
    DependsOn: DevonDisconnectIntegration
  DevWebSocketsendMessage:
    Type: 'AWS::Serverless::Function'
    Properties:
      CodeUri: WebSockets/sendMessage
      Role: 'arn:aws:iam::${AWS::AccountId}:role/AWSLambdaBasicExecutionRole'
      Environment:
        Variables:
          STAGE: Dev
  DevsendMessagePermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: 'lambda:InvokeFunction'
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: DevWebSocketsendMessage
      SourceArn:
        'Fn::Sub':
          - >-
            arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/${__Name__}
          - __Stage__: '*'
            __ApiId__:
              Ref: DevWebSocket
            __Name__: sendMessage
  DevsendMessageIntegration:
    Type: 'AWS::ApiGatewayV2::Integration'
    Properties:
      ApiId:
        Ref: DevWebSocket
      Description: DevsendMessageIntegration
      IntegrationType: AWS_PROXY
      IntegrationUri:
        'Fn::Sub': >-
          arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DevWebSocketsendMessage.Arn}/invocations
      IntegrationMethod: POST
  DevsendMessageRoute:
    Type: 'AWS::ApiGatewayV2::Route'
    Properties:
      ApiId:
        Ref: DevWebSocket
      RouteKey: sendMessage
      OperationName: DevsendMessageRoute
      Target:
        'Fn::Join':
          - /
          - - integrations
            - Ref: DevsendMessageIntegration
    DependsOn: DevsendMessageIntegration

And the authorizer lambda:

const jwt = require('jsonwebtoken')
const request = require('request')
const jwkToPem = require('jwk-to-pem')

const userPoolId = {
  Dev: 'replace with your pool id (like eu-central-1_XXXXX)',
  Prod: 'replace with your pool id (like eu-central-1_XXXXX)'
}[process.env.STAGE]
const region = 'eu-central-1'
const iss = `https://cognito-idp.${region}.amazonaws.com/${userPoolId}`
let pems

exports.handler = function (event, context) {
  console.log('Lambda invoked! event', event, 'context', context)
  //Download PEM for your UserPool if not already downloaded
  if (!pems) {
    //Download the JWKs and save it as PEM
    request({
      url: iss + '/.well-known/jwks.json',
      json: true
    }, function (error, response, body) {
      if (!error && response.statusCode === 200) {
        pems = {}
        const keys = body['keys']
        for (let i = 0; i < keys.length; i++) {
          //Convert each key to PEM
          const key_id = keys[i].kid
          const modulus = keys[i].n
          const exponent = keys[i].e
          const key_type = keys[i].kty
          const jwk = { kty: key_type, n: modulus, e: exponent }
          pems[key_id] = jwkToPem(jwk)
        }
        //Now continue with validating the token
        ValidateToken(pems, event, context)
      } else {
        //Unable to download JWKs, fail the call
        context.fail('error')
      }
    })
  } else {
    //PEMs are already downloaded, continue with validating the token
    ValidateToken(pems, event, context)
  }
}

function ValidateToken (pems, event, context) {

  const token = event.queryStringParameters.token
  //Fail if the token is not jwt
  const decodedJwt = jwt.decode(token, { complete: true })
  if (!decodedJwt) {
    console.log('Not a valid JWT token')
    context.fail('Unauthorized')
    return
  }

  //Fail if token is not from your UserPool
  if (decodedJwt.payload.iss !== iss) {
    console.log('invalid issuer')
    context.fail('Unauthorized')
    return
  }

  //Reject the jwt if it's not an 'ID Token'
  if (decodedJwt.payload.token_use !== 'id') {
    console.log('Not an id token')
    context.fail('Unauthorized')
    return
  }

  //Get the kid from the token and retrieve corresponding PEM
  const kid = decodedJwt.header.kid
  const pem = pems[kid]
  if (!pem) {
    console.log('Invalid access token')
    context.fail('Unauthorized')
    return
  }

  //Verify the signature of the JWT token to ensure it's really coming from your User Pool
  jwt.verify(token, pem, { issuer: iss }, function (err, payload) {
    if (err) {
      console.error('jwt.verify error', err)
      context.fail('Unauthorized')
    } else {
      console.log('Success')
      context.succeed({
        principalId: payload.sub,
        policyDocument: {
          Version: '2012-10-17',
          Statement: [{
            Action: 'execute-api:Invoke',
            Effect: 'Allow',
            Resource: [event.methodArn]
          }]
        },
        context: {
          userID: payload.sub,
          type: payload['custom:type']
        }
      })
    }
  })
}

The things is the authorizer have to be setup ONLY on the route for $connect

Have fun 😉

0reactions
petrucristescucommented, Jun 16, 2022

Why is the “IntegrationMethod: POST” needed?

I’m getting some “Execution failed due to configuration error: Invalid permissions on Lambda function”

This is my Type: AWS::Lambda::Permission

  AuthorizerLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      Principal: apigateway.amazonaws.com
      FunctionName:
        Ref: AuthorizerLambdaFunction
      SourceArn:
        'Fn::Sub':
          - >-
            arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/$connect
          - __Stage__: '*'
            __ApiId__:
              Ref: ServerlessAPI
Read more comments on GitHub >

github_iconTop Results From Across the Web

No results found

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found