AuthorizerConfigurationException в AWS API Gateway / Lambda Custom Authorizer - PullRequest
0 голосов
/ 05 марта 2020

Я создаю службу REST с использованием бессерверной инфраструктуры на AWS Lambda. Я создал собственный авторизатор, который называется pre для любых вызовов моих лямбд. Когда я запускаю без сервера, все работает. При развертывании я получаю сообщение об ошибке в AP Gateway. Я включил вход в API-шлюз, но в журнал ничего не записывается.

Вот мой файл serverless.yml:

provider:
  name: aws
  runtime: nodejs12.x
  stage: ${opt:stage, "dev"}
  region: eu-west-1

functions:

  authorize:
    handler: src/handlers/authorization.authorize

  listAlerts:
    handler: src/handlers/alert-handler.listAlerts
    events:
      - http:
          path: /alerts
          method: GET
          authorizer: ${self:custom.authorization.authorize}

custom:
  stage: ${opt:stage, self:provider.stage}
  authorization:
    authorize:
      name: authorize
      type: TOKEN
      identitySource: method.request.header.Authorization
      identityValidationExpression: Bearer (.*)
plugins:
  - serverless-plugin-typescript
  - serverless-offline
package:
    include:
    - src/**.*

Мой обработчик авторизации выглядит следующим образом. Метод забирает мой токен аутентификации и проверяет его, используя JOSE, и возвращает принципал для пользователя и некоторые роли:

import jwksClient from "jwks-rsa";
import { JWT } from "jose";


export const authorize = async (event: CustomAuthorizerEvent): Promise<CustomAuthorizerResult> => {
    const prefix = "bearer ";
    if (!event.authorizationToken?.toLowerCase().startsWith(prefix)) {
        return Promise.reject("Unauthorized");
    }
    const token = event.authorizationToken?.substring(prefix.length);
    const signingKey = await getCachedSigningKey()
    const jwt = JWT.verify(token, signingKey);
    if ((typeof jwt) !== "object") {
        throw "Unauthorized";
    }
    const userId = jwt["sub"];
    const expires = Number(jwt["exp"]);
    const roles = jwt["assumed-roles"] as string[];
    if (Date.now() > expires * 1000 ) {
        throw "Unauthorized";
    }
    const principalId = userId;
    const policyDocument: PolicyDocument = {
        Version: "2012-10-17",
        Statement: [
            {
                Action: "execute-api:Invoke",
                Effect: "Allow",
                Resource: event.methodArn,
            }
        ]
    };

    return {
        principalId,
        policyDocument,
        context: {
            userId,
            roles
        }
    };
};

Если сделать serverless offline start, эмулируемый API-шлюз запускается на порту 3000. Я вызываю API:

❯ http :3000/alerts/5480e8a1-e3d4-432d-985e-9542c91a49ce Authorization:"Bearer eyJraWQiO.......LHA12jM2UEXFy76dhKUj_iX6SXQQ"
HTTP/1.1 200 OK
Connection: keep-alive
Date: Wed, 04 Mar 2020 21:29:07 GMT
accept-ranges: bytes
access-control-allow-origin: *
cache-control: no-cache
content-length: 174
content-type: application/json; charset=utf-8

{
    "id": "5480e8a1-e3d4-432d-985e-9542c91a49ce",
    "message": "test",
    "subjects": [
        {
            "id": "6ee2c07d-6486-4601-9b4b-05f61c0d0caf",
            "referenceId": "5480e8a1-e3d4-432d-985e-9542c91a49ce"
        }
    ]
}

Итак, он работает локально, и мой обработчик регистрирует userId и роли. Затем я развертываюсь на AWS с serverless deplpy без каких-либо предупреждений. Я пытаюсь вызвать конечную точку моего шлюза API:

❯ http https://og8...<bla-bla>..kcc.execute-api.eu-west-1.amazonaws.com/dev/alerts/alerts/ Authorization:"Bearer eyJraWQiOiJkZXYiL.....VOPI2LHA12jM2UEXFy76dhKUj_iX6SXQQ"
HTTP/1.1 500 Internal Server Error
Connection: keep-alive
Content-Length: 16
Content-Type: application/json
Date: Wed, 04 Mar 2020 21:32:22 GMT
Via: 1.1 e31ab4c27d99cec62ef37e2607db9b45.cloudfront.net (CloudFront)
X-Amz-Cf-Id: kfIhCZHCGoL3OcjPSX4QWdtK1Qequ2vJe9RNst4wBEf_d90fJLjWgQ==
X-Amz-Cf-Pop: ARN1-C1
X-Cache: Error from cloudfront
x-amz-apigw-id: I4mu_HOFjoEF6SQ=
x-amzn-ErrorType: AuthorizerConfigurationException
x-amzn-RequestId: 3cb89b9a-26db-4890-8edb-6aedcc51c09e

{
    "message": null
}

Вызов не возвращается мгновенно, а заканчивается. Что происходит?

Ответы [ 2 ]

0 голосов
/ 05 марта 2020

ОК, отвечая на мой собственный вопрос, у меня было несколько проблем, описанных выше:

  • Невозможно вызвать асинхронную функцию c. const signingKey = await getCachedSigningKey() не удастся (Это может быть отсутствие разрешений для выполнения HTTP вне VP C).
  • Контекст может содержать только простые сопоставления. Я попытался установить массив здесь (x), но это не разрешено. Только простые значения.

Мое окончательное рабочее решение выглядело так, если бы это могло кому-либо помочь:

import { CustomAuthorizerEvent, CustomAuthorizerResult, PolicyDocument } from "aws-lambda";
import { JWT } from "jose";

export enum Role {
    User = "ROLE_USER",
    Admin = "ROLE_ADMIN",
}

export const getAuthorizerForRoles = (requiredRoles: Role[]) => async (event: CustomAuthorizerEvent): Promise<CustomAuthorizerResult> => {
    try {
        const prefix = "bearer ";
        if (!event.authorizationToken?.toLowerCase().startsWith(prefix)) {
            throw new Error(`Token does not start with ${prefix.trim()}`);
        }
        const token = event.authorizationToken?.substring(prefix.length);
            const signingKey = process.env.AUTH_SIGNING_KEY;
        const issuer = process.env.AUTH_ISSUER;
        if (!signingKey || !issuer) {
            throw new Error(`Auth properties not configure correct`);
        }
        const jwt = JWT.verify(token, signingKey, {
            issuer,
        });
        if ((typeof jwt) !== "object") {
            throw new Error(`The JWT has a unexpected structure`);
        }
        const userId = jwt["sub"];
        const expiresEpochMilliSec = Number(jwt["exp"]) * 1000;
        const expires = new Date(expiresEpochMilliSec);
        const assumedRoles = jwt["assumed-roles"] as string[];
        requiredRoles.forEach((requiredRole) => {
            if (!assumedRoles.includes(requiredRole)) {
                throw new Error(`The token does not contain the required role ${requiredRole}`);
            }
        });
        if (Date.now() > expires.valueOf()) {
            throw new Error(`The expired at ${expires}`);
        }
        const principalId = userId;
        const policyDocument: PolicyDocument = {
            Version: "2012-10-17",
            Statement: [
                {
                    Action: "execute-api:Invoke",
                    Effect: "Allow",
                    Resource: event.methodArn,
                }
            ]
        };

        const authResponse: CustomAuthorizerResult = {
            principalId,
            policyDocument,
            context: {
                userId,
                roles: assumedRoles.join(","),
            }
        };
        return authResponse;
    } catch (error) {
        console.log(error);
        throw "Unauthorized";
    }
};

export const authorizeUser = getAuthorizerForRoles([Role.User]);

export const authorizeAdmin = getAuthorizerForRoles([Role.Admin]);

Как обычно, документы и сообщения журнала от AWS пустые. минимальный ..

0 голосов
/ 05 марта 2020
  1. У вас нет такого маршрута / оповещения / 5480e8a1-e3d4-432d-985e-9542c91a49 c в вашей конфигурации
  2. Пожалуйста, откройте консоль API GW и используйте Test для отладки вашей проблемы enter image description here
...