

# Controlar e gerenciar o acesso a APIs de WebSocket no API Gateway
<a name="apigateway-websocket-api-control-access"></a>

O API Gateway é compatível com vários mecanismos para controlar e gerenciar o acesso à sua API WebSocket.

Os mecanismos a seguir podem ser usados para autenticação e autorização:
+ As **funções e políticas padrão do AWS IAM** oferecem controles de acesso flexíveis e robustos. É possível usar as funções e políticas do IAM para controlar quem pode criar e gerenciar suas APIs, bem como quem pode chamá-las. Para obter mais informações, consulte [Controlar o acesso a APIs de WebSocket com autorização do IAM](apigateway-websocket-control-access-iam.md).
+ As **tags do IAM** podem ser usadas com as políticas do IAM para controlar o acesso. Para obter mais informações, consulte [Usar tags para controlar o acesso aos recursos da API REST do API Gateway](apigateway-tagging-iam-policy.md).
+ **Autorizadores do Lambda** são funções do Lambda que controlam o acesso a APIs. Para obter mais informações, consulte [Controlar o acesso a APIs de WebSocket com autorizadores REQUEST do AWS Lambda](apigateway-websocket-api-lambda-auth.md).

Para melhorar seu procedimento de segurança, recomendamos configurar um autorizador para a rota `$connect` em todas as suas APIs de WebSocket. Você pode precisar fazer isso para cumprir vários requisitos de conformidade. Para ter mais informações, consulte [Amazon API Gateway controls](https://docs.aws.amazon.com/securityhub/latest/userguide/apigateway-controls.html) no *Guia do usuário do AWS Security Hub*.

**Topics**
+ [Controlar o acesso a APIs de WebSocket com autorização do IAM](apigateway-websocket-control-access-iam.md)
+ [Controlar o acesso a APIs de WebSocket com autorizadores REQUEST do AWS Lambda](apigateway-websocket-api-lambda-auth.md)

# Controlar o acesso a APIs de WebSocket com autorização do IAM
<a name="apigateway-websocket-control-access-iam"></a>

A autorização do IAM em APIs WebSocket é semelhante à utilizada para [APIs REST](api-gateway-control-access-using-iam-policies-to-invoke-api.md), com as seguintes exceções:
+ A ação `execute-api` oferece suporte a `ManageConnections`, além de ações existentes (`Invoke`, `InvalidateCache`). `ManageConnections` controla o acesso à API @connections.
+ As rotas do WebSocket utilizam um formato diferente de ARN:

  ```
  arn:aws:execute-api:region:account-id:api-id/stage-name/route-key
  ```
+ A API `@connections` utiliza o mesmo formato de ARN presente nas APIs REST:

  ```
  arn:aws:execute-api:region:account-id:api-id/stage-name/POST/@connections
  ```

**Importante**  
Ao usar a [Autorização do IAM](#apigateway-websocket-control-access-iam), é necessário assinar solicitações com o [Signature Version 4 (SigV4)](https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html).

Por exemplo, você pode configurar a política a seguir ao cliente: Este exemplo permite que todas as pessoas enviem uma mensagem (`Invoke`) a todas as rotas, exceto para uma rota secreta no estágio `prod`, além de impedir que qualquer pessoa envie uma mensagem aos clientes conectados (`ManageConnections`) para todos os estágios.

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "execute-api:Invoke"
            ],
            "Resource": [
                "arn:aws:execute-api:us-east-1:111122223333:api-id/prod/*"
            ]
        },
        {
            "Effect": "Deny",
            "Action": [
                "execute-api:Invoke"
            ],
            "Resource": [
                "arn:aws:execute-api:us-east-1:111122223333:api-id/prod/secret"
            ]
        },
        {
            "Effect": "Deny",
            "Action": [
                "execute-api:ManageConnections"
            ],
            "Resource": [
                "arn:aws:execute-api:us-east-1:111122223333:api-id/*"
            ]
        }
    ]
}
```

------

# Controlar o acesso a APIs de WebSocket com autorizadores REQUEST do AWS Lambda
<a name="apigateway-websocket-api-lambda-auth"></a>

Uma função do autorizador do Lambda em APIs WebSocket é semelhante à utilizada para [APIs REST](apigateway-use-lambda-authorizer.md#api-gateway-lambda-authorizer-lambda-function-create), com as seguintes exceções:
+  Você só pode usar uma função de autorizador do Lambda para a rota `$connect`. 
+ Você não pode utilizar variáveis de caminho (`event.pathParameters`), afinal, o caminho é fixo.
+ `event.methodArn` é diferente de sua API REST equivalente, visto que não possui um método HTTP. No caso de `$connect`, `methodArn` termina com `"$connect"`:

  ```
  arn:aws:execute-api:region:account-id:api-id/stage-name/$connect
  ```
+ As variáveis de contexto em `event.requestContext` são diferentes das utilizadas para APIs REST.

 O exemplo a seguir mostra uma entrada para um autorizador `REQUEST` de uma API de WebSocket:

```
{
    "type": "REQUEST",
    "methodArn": "arn:aws:execute-api:us-east-1:123456789012:abcdef123/default/$connect",
    "headers": {
        "Connection": "upgrade",
        "content-length": "0",
        "HeaderAuth1": "headerValue1",
        "Host": "abcdef123.execute-api.us-east-1.amazonaws.com",
        "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits",
        "Sec-WebSocket-Key": "...",
        "Sec-WebSocket-Version": "13",
        "Upgrade": "websocket",
        "X-Amzn-Trace-Id": "...",
        "X-Forwarded-For": "...",
        "X-Forwarded-Port": "443",
        "X-Forwarded-Proto": "https"
    },
    "multiValueHeaders": {
        "Connection": [
            "upgrade"
        ],
        "content-length": [
            "0"
        ],
        "HeaderAuth1": [
            "headerValue1"
        ],
        "Host": [
            "abcdef123.execute-api.us-east-1.amazonaws.com"
        ],
        "Sec-WebSocket-Extensions": [
            "permessage-deflate; client_max_window_bits"
        ],
        "Sec-WebSocket-Key": [
            "..."
        ],
        "Sec-WebSocket-Version": [
            "13"
        ],
        "Upgrade": [
            "websocket"
        ],
        "X-Amzn-Trace-Id": [
            "..."
        ],
        "X-Forwarded-For": [
            "..."
        ],
        "X-Forwarded-Port": [
            "443"
        ],
        "X-Forwarded-Proto": [
            "https"
        ]
    },
    "queryStringParameters": {
        "QueryString1": "queryValue1"
    },
    "multiValueQueryStringParameters": {
        "QueryString1": [
            "queryValue1"
        ]
    },
    "stageVariables": {},
    "requestContext": {
        "routeKey": "$connect",
        "eventType": "CONNECT",
        "extendedRequestId": "...",
        "requestTime": "19/Jan/2023:21:13:26 +0000",
        "messageDirection": "IN",
        "stage": "default",
        "connectedAt": 1674162806344,
        "requestTimeEpoch": 1674162806345,
        "identity": {
            "sourceIp": "..."
        },
        "requestId": "...",
        "domainName": "abcdef123.execute-api.us-east-1.amazonaws.com",
        "connectionId": "...",
        "apiId": "abcdef123"
    }
}
```

A função demonstrativa do autorizador do Lambda a seguir é uma versão WebSocket da função do autorizador do Lambda para APIs REST em [Exemplos adicionais de funções do autorizador do Lambda](apigateway-use-lambda-authorizer.md#api-gateway-lambda-authorizer-lambda-function-create):

------
#### [ Node.js ]

```
   // A simple REQUEST authorizer example to demonstrate how to use request 
   // parameters to allow or deny a request. In this example, a request is  
   // authorized if the client-supplied HeaderAuth1 header and QueryString1 query parameter
   // in the request context match the specified values of
   // of 'headerValue1' and 'queryValue1' respectively.
            export const handler = function(event, context, callback) {
    console.log('Received event:', JSON.stringify(event, null, 2));

   // Retrieve request parameters from the Lambda function input:
   var headers = event.headers;
   var queryStringParameters = event.queryStringParameters;
   var stageVariables = event.stageVariables;
   var requestContext = event.requestContext;
       
   // Parse the input for the parameter values
   var tmp = event.methodArn.split(':');
   var apiGatewayArnTmp = tmp[5].split('/');
   var awsAccountId = tmp[4];
   var region = tmp[3];
   var ApiId = apiGatewayArnTmp[0];
   var stage = apiGatewayArnTmp[1];
   var route = apiGatewayArnTmp[2];
       
   // Perform authorization to return the Allow policy for correct parameters and 
   // the 'Unauthorized' error, otherwise.
   var authResponse = {};
   var condition = {};
    condition.IpAddress = {};
    
   if (headers.HeaderAuth1 === "headerValue1"
       && queryStringParameters.QueryString1 === "queryValue1") {
        callback(null, generateAllow('me', event.methodArn));
    }  else {
        callback(null, generateDeny('me', event.methodArn)); 
    }
}
    
// Helper function to generate an IAM policy
var generatePolicy = function(principalId, effect, resource) {
   // Required output:
   var authResponse = {};
    authResponse.principalId = principalId;
   if (effect && resource) {
       var policyDocument = {};
        policyDocument.Version = '2012-10-17		 	 	 '; // default version
       policyDocument.Statement = [];
       var statementOne = {};
        statementOne.Action = 'execute-api:Invoke'; // default action
       statementOne.Effect = effect;
        statementOne.Resource = resource;
        policyDocument.Statement[0] = statementOne;
        authResponse.policyDocument = policyDocument;
    }
   // Optional output with custom properties of the String, Number or Boolean type.
   authResponse.context = {
       "stringKey": "stringval",
       "numberKey": 123,
       "booleanKey": true
    };
   return authResponse;
}
    
var generateAllow = function(principalId, resource) {
   return generatePolicy(principalId, 'Allow', resource);
}
    
var generateDeny = function(principalId, resource) {
   return generatePolicy(principalId, 'Deny', resource);
}
```

------
#### [ Python ]

```
# A simple REQUEST authorizer example to demonstrate how to use request
# parameters to allow or deny a request. In this example, a request is
# authorized if the client-supplied HeaderAuth1 header and QueryString1 query parameter
# in the request context match the specified values of
# of 'headerValue1' and 'queryValue1' respectively.

import json


def lambda_handler(event, context):
    print(event)

    # Retrieve request parameters from the Lambda function input:
    headers = event['headers']
    queryStringParameters = event['queryStringParameters']
    stageVariables = event['stageVariables']
    requestContext = event['requestContext']

    # Parse the input for the parameter values
    tmp = event['methodArn'].split(':')
    apiGatewayArnTmp = tmp[5].split('/')
    awsAccountId = tmp[4]
    region = tmp[3]
    ApiId = apiGatewayArnTmp[0]
    stage = apiGatewayArnTmp[1]
    route = apiGatewayArnTmp[2]

    # Perform authorization to return the Allow policy for correct parameters
    # and the 'Unauthorized' error, otherwise.

    authResponse = {}
    condition = {}
    condition['IpAddress'] = {}

    if (headers['HeaderAuth1'] ==
            "headerValue1" and queryStringParameters["QueryString1"] == "queryValue1"):
        response = generateAllow('me', event['methodArn'])
        print('authorized')
        return json.loads(response)
    else:
        response = generateDeny('me', event['methodArn'])
        print('unauthorized')
        return json.loads(response)

    # Help function to generate IAM policy


def generatePolicy(principalId, effect, resource):
    authResponse = {}
    authResponse['principalId'] = principalId
    if (effect and resource):
        policyDocument = {}
        policyDocument['Version'] = '2012-10-17		 	 	 '
        policyDocument['Statement'] = []
        statementOne = {}
        statementOne['Action'] = 'execute-api:Invoke'
        statementOne['Effect'] = effect
        statementOne['Resource'] = resource
        policyDocument['Statement'] = [statementOne]
        authResponse['policyDocument'] = policyDocument

    authResponse['context'] = {
        "stringKey": "stringval",
        "numberKey": 123,
        "booleanKey": True
    }

    authResponse_JSON = json.dumps(authResponse)

    return authResponse_JSON


def generateAllow(principalId, resource):
    return generatePolicy(principalId, 'Allow', resource)


def generateDeny(principalId, resource):
    return generatePolicy(principalId, 'Deny', resource)
```

------

Para configurar a função anterior do Lambda como uma função do autorizador `REQUEST` para uma API WebSocket, siga o mesmo procedimento para [APIs REST](configure-api-gateway-lambda-authorization.md#configure-api-gateway-lambda-authorization-with-console).

Para configurar a rota `$connect` a fim de esse autorizador do Lambda no console, selecione ou crie a rota `$connect`. Na seção **Configurações de solicitação de rota**, selecione **Editar**. Selecione o autorizador no menu suspenso **Autorização** e escolha **Salvar alterações**.

Para testar o autorizador, é necessário uma conexão. O autorizador da alteração no `$connect` não afeta o cliente já conectado. Quando você se conectar à API WebSocket, será necessário fornecer valores para todas as origens de identidade configuradas. Por exemplo, você pode se conectar ao enviar uma string de consulta e cabeçalho validos usando `wscat` como no exemplo a seguir:

```
wscat -c 'wss://myapi.execute-api.us-east-1.amazonaws.com/beta?QueryString1=queryValue1' -H HeaderAuth1:headerValue1
```

Ao tentar se conectar sem um valor de identidade válido, você receberá uma resposta `401`:

```
wscat -c wss://myapi.execute-api.us-east-1.amazonaws.com/beta
error: Unexpected server response: 401
```