

# Control y administración del acceso a las API de WebSocket en API Gateway
<a name="apigateway-websocket-api-control-access"></a>

API Gateway ofrece diversos mecanismos para controlar y administrar el acceso a su API de WebSocket.

Puede utilizar los siguientes mecanismos para realizar la autenticación y autorización:
+ **Los roles y las políticas estándar de AWS IAM** ofrecen controles de acceso flexibles y robustos. Puede usar roles y políticas de IAM para controlar quién puede crear y administrar sus API, así como quién puede invocarlas. Para obtener más información, consulte [Control del acceso a las API de WebSocket con autorización de IAM](apigateway-websocket-control-access-iam.md).
+ Las **etiquetas de IAM** se pueden utilizar con políticas de IAM para controlar el acceso. Para obtener más información, consulte [Uso de etiquetas para controlar el acceso a los recursos API de REST de API Gateway](apigateway-tagging-iam-policy.md).
+ Los **autorizadores de Lambda** son funciones Lambda que controlan el acceso a las API. Para obtener más información, consulte [Control del acceso a las API de WebSocket con autorizadores REQUEST de AWS Lambda](apigateway-websocket-api-lambda-auth.md).

Para aumentar la seguridad, le recomendamos que configure un autorizador para la ruta de `$connect` en todas las API de WebSocket. Es probable que deba hacerlo para asegurar el cumplimiento con diversos marcos normativos. Para obtener más información, consulte [Controles de Amazon API Gateway](https://docs.aws.amazon.com/securityhub/latest/userguide/apigateway-controls.html) en la *Guía del usuario de AWS Security Hub*.

**Topics**
+ [Control del acceso a las API de WebSocket con autorización de IAM](apigateway-websocket-control-access-iam.md)
+ [Control del acceso a las API de WebSocket con autorizadores REQUEST de AWS Lambda](apigateway-websocket-api-lambda-auth.md)

# Control del acceso a las API de WebSocket con autorización de IAM
<a name="apigateway-websocket-control-access-iam"></a>

La autorización de IAM en las API de WebSocket es similar a la de las [API REST](api-gateway-control-access-using-iam-policies-to-invoke-api.md), con las siguientes excepciones:
+ La acción `execute-api` admite `ManageConnections` además de las acciones existentes (`Invoke`, `InvalidateCache`). `ManageConnections` controla el acceso a la API @connections.
+ Las rutas de WebSocket utilizan un formato de ARN distinto:

  ```
  arn:aws:execute-api:region:account-id:api-id/stage-name/route-key
  ```
+ La API `@connections` utiliza el mismo formato de ARN que las API de REST:

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

**importante**  
Cuando utilice la [Autorización de IAM](#apigateway-websocket-control-access-iam), deberá firmar las solicitudes con [Signature Version 4 (SigV4)](https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html).

Por ejemplo, podría configurar la siguiente política en el cliente. Este ejemplo permite a cualquier usuario enviar un mensaje (`Invoke`) a todas las rutas excepto a una ruta secreta en la etapa `prod` e impide que se manden mensajes a los clientes conectados (`ManageConnections`) en todas las etapas.

------
#### [ 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/*"
            ]
        }
    ]
}
```

------

# Control del acceso a las API de WebSocket con autorizadores REQUEST de AWS Lambda
<a name="apigateway-websocket-api-lambda-auth"></a>

La función de autorizador de Lambda en las API de WebSocket es similar a la de las [API de REST](apigateway-use-lambda-authorizer.md#api-gateway-lambda-authorizer-lambda-function-create), con las siguientes excepciones:
+  Solo puede utilizar una función de autorizador de Lambda para la ruta `$connect`. 
+ No se pueden utilizar variables de ruta (`event.pathParameters`), ya que la ruta es fija.
+ `event.methodArn` es diferente de su equivalente en la API de REST, ya que no tiene ningún método HTTP. En el caso de `$connect`, `methodArn` termina por `"$connect"`:

  ```
  arn:aws:execute-api:region:account-id:api-id/stage-name/$connect
  ```
+ Las variables de contexto de `event.requestContext` son diferentes de las de las API de REST.

 En el siguiente ejemplo se muestra una entrada a un autorizador `REQUEST` para una 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"
    }
}
```

El siguiente ejemplo de función de autorizador de Lambda es una versión de WebSocket de la función de autorizador de Lambda para las API de REST de la sección [Ejemplos adicionales de funciones del autorizador de 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 la función de Lambda anterior como una función de autorizador `REQUEST` para una API de WebSocket, siga el mismo procedimiento que para las [API de REST](configure-api-gateway-lambda-authorization.md#configure-api-gateway-lambda-authorization-with-console).

Para configurar la ruta `$connect` de forma que utilice este autorizador de Lambda en la consola, seleccione o cree la ruta `$connect`. En la sección **Configuración**, elija **Editar**. Seleccione su autorizador en el menú desplegable **Autorización** y, a continuación, elija **Guardar cambios**.

Para probar el autorizador, tiene que crear una conexión nueva. El cambio de autorizador en `$connect` no afecta a los clientes que ya están conectados. Al conectarse a la API de WebSocket, tiene que proporcionar valores para todas las fuentes de identidad configuradas. Por ejemplo, puede conectarse enviando una cadena de consulta y un encabezado válidos mediante `wscat`, como se muestra en el siguiente ejemplo:

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

Si intenta conectarse sin un valor de identidad válido, recibirá una respuesta `401`:

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