Cognito Authorization for Lambda REST API

Cognito Authorization

AWS Amplify: Cognito Authorization for Lambda REST API — Part 3

Cognito Authorization — Con la capa Lambda de la Parte 1 y la función Lambda de la Parte 2, ¡finalmente sinteticemos todo en una API REST autorizada por Cognito a la que se puede llamar desde nuestro cliente para realizar una eliminación en cascada en una tabla en nuestra API GraphQL!


Con nuestra capa Lambda terminada y nuestra función Lambda sin servidor de Clothe express construida, finalmente agreguemos la API REST para llamar a la función de eliminación en cascada para nuestra tabla de Clothes de nuestro cliente.

Agregue la API desde la línea de comando:

$ amplify add api
? Select from one of the below mentioned services: REST
✔ Provide a friendly name for your resource to be used as a label for this category in the project: · <my-wardrobe-app>api
✔ Provide a path (e.g., /book/{isbn}): · /clothe/{clotheId}
✔ Choose a Lambda source · Use a Lambda function already added in the current Amplify project
✔ Choose the Lambda function to invoke by this path · clotheAPI
✔ Restrict API access? (Y/n) · yes
✔ Restrict access by: · Auth/Guest Users
✔ Who should have access? · Authenticated users only
✔ What permissions do you want to grant to Authenticated users? · delete
✔ Do you want to add another path? (y/N) · no
✅ Successfully added resource <my-wardrobe-app>api locally

En un mundo ideal, esto sería todo lo que necesitaríamos para que nuestra API REST se ponga en marcha (después de amplify push, por supuesto). Pero, por desgracia, ese no es el caso.

Esto creará la API REST, pero la verificación del grupo de usuarios de Cognito no funcionará a menos que (a) cambiemos manualmente el Autorizador de cada ruta en la consola de AWS API Gateway a uno nuevo personalizado basado en nuestro grupo de usuarios de Cognito cada vez que actualicemos esto API o (b) anular la plantilla de CloudFormation para hacer esto por nosotros.

Obviamente, iremos con (b). Afortunadamente, el código de anulación es bastante simple. Necesitamos decirle a Amplify que estamos agregando un código de anulación a nuestra API ejecutando el siguiente comando y seleccionando nuestra API REST creada anteriormente:

$ amplify override api

Esto creará un archivo llamado override.ts en amplify/backend/api/<my-wardrobe-app>api/. Ingrese allí y copie el siguiente código, asegurándose de reemplazar <your auth name here> en la línea 19 con el nombre del recurso de auth de su proyecto:

// This file is used to override the REST API resources configuration
import { AmplifyApiRestResourceStackTemplate } from "@aws-amplify/cli-extensibility-helper";

export function override(resources: AmplifyApiRestResourceStackTemplate) {
  // Add our user pool id as a parameter so we can create authorizers with it
  // Note that you have to replace <your auth name here> with the name of your auth!
  // It's the name of the folder in amplify/backend/auth that was created when you
  // added the auth to the project (NOT userPoolGroups). Also make sure you keep
  // the preceding "auth" part of the string before the auth name, it's necessary.
  resources.addCfnParameter(
    {
      type: "String",
      description:
        "The id of an existing User Pool to connect. If this is changed, a user pool will not be created for you.",
      default: "NONE",
    },
    "AuthCognitoUserPoolId",
    {
      "Fn::GetAtt": ["auth<your auth name here>", "Outputs.UserPoolId"],
    }
  );
  // create the authorizer using the AuthCognitoUserPoolId parameter defined above
  resources.restApi.addPropertyOverride("Body.securityDefinitions", {
    Cognito: {
      type: "apiKey",
      name: "Authorization",
      in: "header",
      "x-amazon-apigateway-authtype": "cognito_user_pools",
      "x-amazon-apigateway-authorizer": {
        type: "cognito_user_pools",
        providerARNs: [
          {
            "Fn::Join": [
              "",
              [
                "arn:aws:cognito-idp:",
                {
                  Ref: "AWS::Region",
                },
                ":",
                {
                  Ref: "AWS::AccountId",
                },
                ":userpool/",
                {
                  Ref: "AuthCognitoUserPoolId",
                },
              ],
            ],
          },
        ],
      },
    },
  });
  // for each path in the rest API, add the authorizer for all methods
  for (const path in resources.restApi.body.paths) {
    // add the Authorization header as a parameter to the rest API for the path
    resources.restApi.addPropertyOverride(
      `Body.paths.${path}.x-amazon-apigateway-any-method.parameters`,
      [
        ...resources.restApi.body.paths[path]["x-amazon-apigateway-any-method"]
          .parameters,
        {
          name: "Authorization",
          in: "header",
          required: false,
          type: "string",
        },
      ]
    );
    // set the security method to use our user pool authorizer
    // TODO: do we need to destructure the other security methods as well?
    resources.restApi.addPropertyOverride(
      `Body.paths.${path}.x-amazon-apigateway-any-method.security`,
      [
        {
          Cognito: [],
        },
      ]
    );
  }
}

 

Lo que hace este archivo de anulación de CDK es crear un autorizador en todos los métodos en nuestra API REST en función del grupo de usuarios de Cognito que hemos configurado con nuestro proyecto AWS Amplify

Muchas gracias a @johnEthicalTechnology en GitHub por esta gran solución

Con la API REST anulada correctamente, ahora hemos restringido el acceso de los usuarios a la API solo a los usuarios autorizados en función de nuestro grupo de usuarios de Cognito. Además, el encabezado de Autorización que busca es el Token JWT de Cognito con el formato Authorization: Bearer XXXX, que es lo que hemos configurado para que analice nuestra función Lambda. Así que finalmente es el momento:

¡Empuje para amplificar! amplify push --yes

Suponiendo que todo vaya bien, ahora puede ejecutar la eliminación en cascada a través de la biblioteca de la API de Javascript de AWS Amplify mientras inicia sesión con una sesión actual válida:

import { Auth, API } from "aws-amplify";

// ...

const id = "<some-clothe-id>";
const myInit = {
  headers: {
    Authorization: `Bearer ${(await Auth.currentSession())
      .getIdToken()
      .getJwtToken()}`,
  },
};
const resp = await API.del("<my-wardrobe-api>", `/clothe/${id}`, myInit);

// ...

Tenga en cuenta cómo agregamos el encabezado de Authorization a la solicitud; esto es muy importante para asegurarnos de que la solicitud pase a través de nuestro Autorizador que agregamos en la anulación de CloudFormation.

¡Eso es todo! Ahora tiene un flujo de trabajo de eliminación en cascada remota que se ejecuta independientemente de su cliente.

Cuando un usuario que ha iniciado sesión envía una solicitud DELETE al extremo de la API con el token JWT de ID de su sesión, como en la línea 13, la función Lambda utiliza consultas GraphQL con el alcance correcto para eliminar todas las entradas colaterales de la tabla.

También podemos usar la lista devuelta de identificadores de Clothe y Outfit para realizar la invalidación de etiquetas si estamos usando algún tipo de biblioteca de almacenamiento en caché de API como RTK Query.

Esperemos que esta pequeña serie haya sido útil. Sé que seguramente me habría ahorrado mucho sudor y lágrimas si el conocimiento sobre esto hubiera sido un poco más fácil de conseguir.

Recent Post