Agregar Autorización a Api Gateway HTTP
Tags: aws, lambda
Published: 2022-04-18 16:27:56

En este post vamos a profundizar un poco más en el uso de **Api GateWay Http** y vamos a ver cómo agregar un método de autorización de acceso a los métodos de nuestro Api.

Vamos a partir del ejemplo realizado en el siguiente post:
[Api Gateway Http](https://dev.to/cecamilo/api-gateway-http-api-2f4n) en donde creamos un Api que hace referencia a servicios externos. Este Api no tiene ningún control de acceso por lo que cualquier persona puede consumirlo sin ninguna restricción. en esta ocasión para tener acceso al Api vamos a tener que enviar una cabecera de autorización para tener acceso a sus métodos.

Pasos a realizar:

1.Creación función **Lambda** para rotar el secreto.
2.Creación de Secreto.
3.Crear función lamba de autorización.
4.Creación de autorizar en **Api Gateway**.
5.Asociar autorizador.


Vamos a comenzar creando la función **Lambda** que va a ser la encargada de rotar nuestro secreto.

![Rotate Secret]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/crear lambda rotar secreto.png)

Una vez creada nuestra función vamos a la pestaña de código y copiamos lo siguiente:

```
import json
import uuid
import boto3

client = boto3.client('secretsmanager')

def lambda_handler(event, context):
    # TODO implement
    secret = { "secret_header":str(uuid.uuid4())}
    id = 'reemplazar por arn del secreto'
    client.update_secret(SecretId = id,SecretString=json.dumps(secret))

```
Después de tener nuestro código damos click en Deploy para que nuestros cambios queden guardados.

![Deploy Rotate Secret]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/deploy lambda rotate-secret.png)

Hasta el momento hemos creado la función **Lambda** que va a ser la encargada de rotar el valor del secreto que va servir para la autorización a nuestra api. Sin embargo para que **Secrets Manager** pueda invocar la función debemos asignarle al Rol de la función la Política que le permita realizar esta acción. 

Para este vamos a la pestaña de configuración y damos click en el nombre del Rol.

![Select]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/select%20rol.png)

Ya en la pantalla del ROl vamos a Add Permissions - Attach Policies.

![Attach Policy]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/attach%20policy.png)

En la barra de búsqueda colocamos _Secrests_ ,seleccionamos SecretsManagerReadWritey damos click en _Attach policies_

![Attach Policy1]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/attach%20policy%20rol.png)

![Finish Attach Policy1]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/finish%20attach%20policy.png)

Ya que la función **Lambda** tiene el rol adecuado para modificar nuestro secreto cuando la ejecutemos, el siguiente paso es agregar una politica a la función **Lambda** para que pueda ser llamada desde **Secrets Manager**. Para esto debemos ejecutar el siguiente comando en la terminal reemplazando _ARN_of_lambda_function_ por el ARN de la función Lambda que acabamos de crear.

aws lambda add-permission --function-name _ARN_of_lambda_function_ --principal secretsmanager.amazonaws.com --action lambda:InvokeFunction --statement-id SecretsManagerAccess

Terminada la configuración anterior, nuestra función **Lambda** ya puede realizar modificaciones a nuestro secreto.

El siguiente paso es definir el secreto que vamos a utilizar para autorizar el acceso a nuestro Api. Para esto buscamos en la barra de búsqueda de servicios: **Secrets Manager ** y en la pantalla principal damos click en _Store a new Secret_

![Store a new Secret]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/store%20a%20new%20secret.png)

Empezamos por definir el tipo de secreto y el nombre y valor por defecto que vamos a establecer a nuestro secreto.

![Key Value Secret]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/key%20value%20secret.png)

Definimos un nombre para el secreto: _secret-header-api_.

![Key Value Secret]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/secret%20name.png)

Ahora definimos cada cuando queremos que nuestro secreto cambie (1 día) y qué función Lambda (la que creamos con anterioridad) va a ser la encargada de realizar la rotación.

![Rotation Secret]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/rotation%20secret.png)

La siguiente pantalla nos va a mostrar el resumen de la configuración de nuestro secret, damos click en _Store_.

![Store Secret]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/store%20secret.png)

En la pantalla del listado de secretos damos click en el secreto que acabamos de crear.

![Store Secret]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/list%20secret.png)

En la pantalla debemos copiar el ARN del secreto para reemplazarlo en la variable _id_ de la función Lambda que creamos. Con el objetivo que cuando ejecutemos la función Lambda modifique el valor de nuestro secreto.

Adicional podemos ver el valor del secreto dando click en _Retrive secret value_.

![ARN Secret]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/arn%20secret.png)

En la parte inferior de la pantalla vamos a encontrar el código fuente para obtener el secreto utilizando el lenguaje de programación de nuestra preferencia, que para nuestro caso es Python.

![ARN Secret]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/language%20secret.png)

Hasta el momento hemos creado un secreto con un tiempo definido para su rotación y una función **Lambda** que es la encargada de cambiar el valor de nuestro secreto.

El siguiente paso es crear la función **Lambda** que va autorizar el acceso a nuestro Api.

El siguiente código es el que debe tener nuestra función:

```
import json
import boto3
import base64
from botocore.exceptions import ClientError
# Use this code snippet in your app.
# If you need more information about configurations or implementing the sample code, visit the AWS docs:   
# https://aws.amazon.com/developers/getting-started/python/


secret_name = "arn:aws:secretsmanager:us-east-1:385033107168:secret:secret-header-api4-Yt9jGg"
region_name = "us-east-1"
# Create a Secrets Manager client
session = boto3.session.Session()
client = session.client(service_name='secretsmanager',region_name=region_name)


def get_secret():
    secret =""
    # In this sample we only handle the specific exceptions for the 'GetSecretValue' API.
    # See https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
    # We rethrow the exception by default.

    try:
        get_secret_value_response = client.get_secret_value(
            SecretId=secret_name
        )
    except ClientError as e:
        if e.response['Error']['Code'] == 'DecryptionFailureException':
            # Secrets Manager can't decrypt the protected secret text using the provided KMS key.
            # Deal with the exception here, and/or rethrow at your discretion.
            raise e
        elif e.response['Error']['Code'] == 'InternalServiceErrorException':
            # An error occurred on the server side.
            # Deal with the exception here, and/or rethrow at your discretion.
            raise e
        elif e.response['Error']['Code'] == 'InvalidParameterException':
            # You provided an invalid value for a parameter.
            # Deal with the exception here, and/or rethrow at your discretion.
            raise e
        elif e.response['Error']['Code'] == 'InvalidRequestException':
            # You provided a parameter value that is not valid for the current state of the resource.
            # Deal with the exception here, and/or rethrow at your discretion.
            raise e
        elif e.response['Error']['Code'] == 'ResourceNotFoundException':
            # We can't find the resource that you asked for.
            # Deal with the exception here, and/or rethrow at your discretion.
            raise e
    else:
        # Decrypts secret using the associated KMS key.
        # Depending on whether the secret is a string or binary, one of these fields will be populated.
        if 'SecretString' in get_secret_value_response:
            secret = get_secret_value_response['SecretString']
        else:
            secret = base64.b64decode(get_secret_value_response['SecretBinary'])
            
    # Your code goes here. 
    return json.loads(secret)["secret_header"]


def lambda_handler(event, context):
    
    secret = get_secret()
    # TODO implement
    if event["headers"]["authorization"] == secret:
        return {
            "isAuthorized": True
        }
    else:
        return {
            "isAuthorized": False
        }
```

Básicamente el código es muy parecido al que nos muestra Secrets Manager para obtener nuestro secreto, solamente se le hicieron algunas modificaciones.
Debemos modificar el Rol de la función Lambda para que nos permita obtener el secreto.Debemos seguir los mismos pasos que realizamos con la función **Lambda** definida anteriormente.

Para finalizar debemos crear nuestro autorizador y asociarlo a los métodos que creamos convenientes.

Vamos a API Gateway y buscamos nuestro API y damos click.

![List API]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/list%20api.png)

Ya en la pagina principal de nuestro API, vamos al menu: _Authorization_, buscamos la opción _Manage authorizers_ y damos click en el botón  _Create_.

![Create Authorizer]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/begin%20create%20authorizer.png)

En la configuración de nuestro Autorizar realizamos lo siguiente:

![Configure Authorizer]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/create%20authorizer.png)

Lo que hicimos fue definir, que nuestro autorizador va a ser la función Lambda que creamos, que el tipo de respuesta es simple (json con un valor True o False) ,que nuestra respuesta no va estar en cache y le dimos autorización a API Gateway para que pueda hacer llamados a nuestra función Lambda.

El siguiente paso es asociar nuestro autorizador a las rutas que necesitemos. Vamos al tab _Attach authorizers to routes_, seleccionamos la ruta a la cual queremos asociarle el autorizardor, seleccionamos el autorizar y damos click en el boton _Attach authorizer_.

![Attach Authorizer]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/attach%20authorizer.png)

Ahora podemos ver al lado del tipo de método que nuestra ruta cuenta con un autorizador.

![Attach Authorizer]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/ruta%20authorizer.png)

Con esto finalizamos la configuración de agregar un método de autorización para las rutas o métodos de nuestro API.Vamos a realizar las pruebas utilizando PostMan.Agregamos el header Authorization y colocamos el valor del secreto para realizar la prueba.

![Test Authorizer]
(https://raw.githubusercontent.com/cecamilo00/posts/circleci-project-setup/api gateway http authorization/test%20api.png)

Como siguiente paso pueden realizar un llamado al Api con su lenguaje de programación favorito para validar su funcionamiento.

Una función Lambda en Python seria:

```
import json
import requests

def lambda_handler(event, context):

    r=requests.get("https://d95ca2fimh.execute-api.us-east-1.amazonaws.com/api/timezone", headers={"Authorization":"6a7acb2b-5c79-4951-b051-503dca83225c"})
    # TODO implement
    print(r.json())
    return {
        'statusCode': 200,
        'body': r.json()
    }
```


Me pueden encontrar en

[Camilo Cabrales](https://www.linkedin.com/in/camilo-cabrales-31a0bb53/)

# Referencias
[Permisos](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-required-permissions-function.html)

[Lambda Authorizer](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html)

[Boto3](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html?highlight=secretsmanager#SecretsManager.Client.update_secret)