Другое

Авторизация AWS AppSync: Полное руководство по OPENID_CONNECT и API_KEY

Узнайте, как правильно настроить AWS AppSync с методами авторизации OPENID_CONNECT и API_KEY. Исправьте проблемы с нулевой идентификацией и разрешите конфликты с помощью нашего полного руководства по устранению неполадок.

Проблема с авторизацией в AWS AppSync: конфликт OPENID_CONNECT и API_KEY

Я столкнулся с проблемой в моем приложении AWS AppSync, где добавление метода авторизации API_KEY нарушило существующие маршруты OPENID_CONNECT. Вот детали:

Описание проблемы

  • Мое приложение AWS AppSync корректно работало с авторизацией OPENID_CONNECT
  • При добавлении API_KEY в качестве дополнительного метода авторизации, это нарушило существующие маршруты
  • В схеме AppSync все маршруты ранее содержали директивы @aws_oidc и @aws_api_key
  • После изменений, когда запросы достигают конвейера резолверов:
    • идентификатор приходит как null
    • заголовок x-api-key вставляется во все маршруты

Попытки решения

  • Я пытался удалить @aws_api_key из всех маршрутов и типов, так как мы ранее не использовали API_KEY
  • Это привело к ошибкам Unauthorized

Дополнительный контекст

  • Я отладил CloudFront Lambda Edge и подтвердил, что OPENID токен нормально передается из React frontend
  • Проблема возникает именно тогда, когда резолверы пытаются определить идентификатор для валидации
  • Сохранение директивы @aws_api_key приводит к полному сбою из-за null идентификатора

Вопрос

Как правильно настроить AWS AppSync для поддержки обоих методов авторизации - OPENID_CONNECT и API_KEY, не нарушая существующую функциональность, и почему идентификатор приходит как null при наличии обоих методов авторизации?

AWS AppSync не может корректно обрабатывать несколько методов авторизации одновременно в одном конвейере резолверов, поэтому вы сталкиваетесь с тем, что идентификатор приходит как null при наличии директив OPENID_CONNECT и API_KEY одновременно. Система авторизации разработана для оценки методов в определенном порядке, и при наличии конфликтующих директив оценка может завершиться ошибкой или вернуть null-идентификаторы. Чтобы поддержать оба метода авторизации, необходимо разделить их, создав разные резолверы или используя авторизацию на уровне API Gateway до того, как запросы достигнут AppSync.

Содержание

Поведение авторизации AWS AppSync

AWS AppSync оценивает методы авторизации в определенном порядке, когда в резолвере присутствуют несколько директив. Система авторизации обрабатывает эти директивы последовательно, и если любая директива завершается ошибкой или конфликтует с другими, это может привести к установке идентификатора в null.

Когда у вас есть директивы @aws_oidc и @aws_api_key на одном резолвере, AppSync пытается проверить запрос с использованием обоих методов одновременно. Это создает конфликт, потому что:

  • OPENID_CONNECT ожидает проверку JWT-токена
  • API_KEY ожидает ключ API в заголовках
  • Система авторизации не может определить, какой метод должен иметь приоритет

Ключевое замечание: Авторизация в AppSync разработана для работы с одним основным методом авторизации на резолвер или API, а не с несколькими методами, оцениваемыми одновременно.

Почему идентификатор становится null при нескольких методах

Проблема с null-идентификатором возникает из-за того, как AppSync обрабатывает директивы авторизации:

  1. Порядок авторизации: AppSync оценивает методы авторизации в определенном порядке (обычно сначала API_KEY, затем другие методы)
  2. Конфликт проверки: Когда оба метода присутствуют, система может частично проверить один метод, но не завершить проверку для другого
  3. Вмешательство заголовков: Вставка заголовка x-api-key указывает на то, что срабатывает авторизация API_KEY, потенциально переопределяя или конфликтуя с проверкой OIDC-токена
  4. Контекст резолвера: Переменная $identity становится null, когда система авторизации не может успешно аутентифицировать запрос через какой-либо один метод

Важно: Согласно документации AWS, при настройке нескольких методов авторизации используется только первый успешно аутентифицированный метод, а конфликты могут приводить к сбоям аутентификации.

Правильная конфигурация для смешанной авторизации

Метод 1: Отдельные резолверы для разных типов авторизации

Наиболее надежный подход — создание отдельных резолверов для разных сценариев авторизации:

graphql
# Схема с отдельными типами авторизации
type Query {
  # Для запросов с аутентификацией API_KEY
  getPublicData: [String] @aws_api_key
  
  # Для запросов с аутентификацией OPENID_CONNECT  
  getPrivateData: [String] @aws_oidc
}

type Mutation {
  # Мутация с API_KEY
  updatePublicData(id: ID!, data: String!): UpdateResult 
    @aws_api_key
    
  # Мутация с OIDC  
  updatePrivateData(data: String!): UpdateResult
    @aws_oidc
}

Метод 2: Использование API Gateway как слоя авторизации

Реализуйте авторизацию на уровне API Gateway до того, как запросы достигнут AppSync:

yaml
# Пример конфигурации API Gateway
Resources:
  AppSyncAPI:
    Type: AWS::AppSync::GraphQLApi
    Properties:
      AuthenticationType: API_KEY
      Name: "AppSync with Gateway Auth"
  
  ApiGateway:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: "Auth Gateway"
      
  ApiGatewayStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      RestApiId: !Ref ApiGateway
      StageName: "prod"
      AccessLogSetting:
        DestinationArn: !Ref LogGroup
        Format: "$context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] \"$context.httpMethod $context.resourcePath $context.protocol\" $context.status $context.responseLength $context.requestId"
      MethodSettings:
        - ResourcePath: "/*"
          HttpMethod: "*"
          DataTraceEnabled: true
          LoggingLevel: INFO
          MetricsEnabled: true

Метод 3: Условная авторизация в резолверах

Используйте логику резолвера для обработки разных типов авторизации:

javascript
// Шаблон резолвера VTL для смешанной авторизации
#if($context.identity.claims)
    // Аутентификация OIDC - обработка утверждений JWT
    {
        "version": "2018-05-29",
        "operation": "Invoke",
        "payload": {
            "userId": "$context.identity.claims.sub",
            "email": "$context.identity.claims.email"
        }
    }
#elseif($context.identity.apiKey)
    // Аутентификация по API ключу
    {
        "version": "2018-05-29", 
        "operation": "Invoke",
        "payload": {
            "apiKey": "$context.identity.apiKey",
            "source": "api-key"
        }
    }
#else
    // Нет действительной аутентификации
    {
        "version": "2018-05-29",
        "operation": "Invoke", 
        "payload": {
            "error": "Unauthorized - No valid authentication method"
        }
    }
#end

Альтернативные подходы

1. Использование нескольких API AppSync

Создайте отдельные API AppSync для разных требований авторизации:

yaml
Resources:
  AppSyncPublicAPI:
    Type: AWS::AppSync::GraphQLApi
    Properties:
      AuthenticationType: API_KEY
      Name: "Public AppSync API"
      
  AppSyncPrivateAPI:
    Type: AWS::AppSync::GraphQLApi
    Properties:
      AuthenticationType: OIDC
      Name: "Private AppSync API"
      OpenIDConnectConfig:
        AuthTTL: 900
        ClientId: !GetAtt OIDCApp.ClientId
        IatTTL: 3600
        Issuer: "https://your-oidc-provider.com"

2. Авторизация Lambda@Edge

Реализуйте логику авторизации на краю CloudFront:

javascript
// Функция Lambda@Edge для авторизации
exports.handler = async (event) => {
    const request = event.Records[0].cf.request;
    
    // Проверка наличия OIDC-токена
    const authHeader = request.headers['authorization'] ? 
        request.headers['authorization'][0].value : null;
    
    if (authHeader && authHeader.startsWith('Bearer ')) {
        // Обработка OIDC-токена
        const token = authHeader.substring(7);
        // Проверка токена и переадресация в AppSync с правильными заголовками
        request.headers['x-amzn-oidc-data'] = [{ key: 'x-amzn-oidc-data', value: token }];
    } else if (request.headers['x-api-key']) {
        // Обработка API ключа
        // Переадресация как есть в конечную точку AppSync с авторизацией API_KEY
    } else {
        return {
            status: '401',
            statusDescription: 'Unauthorized',
            headers: {
                'www-authenticate': [{
                    key: 'www-authenticate',
                    value: 'Bearer, ApiKey'
                }]
            }
        };
    }
    
    return request;
};

Шаги по устранению неполадок

Шаг 1: Проверка конфигурации авторизации

  1. Проверьте конфигурацию вашего API AppSync в консоли AWS
  2. Убедитесь, что в качестве основного метода авторизации выбран только один метод
  3. Проверьте, что в резолверах нет конфликтующих директив авторизации

Шаг 2: Тестирование отдельных методов авторизации

Тестируйте каждый метод авторизации отдельно:

bash
# Тест с API ключом
curl -H "x-api-key: YOUR_API_KEY" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"query": "query { getPublicData }"}' \
     YOUR_APPSYNC_ENDPOINT

# Тест с OIDC-токеном
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"query": "query { getPrivateData }"}' \
     YOUR_APPSYNC_ENDPOINT

Шаг 3: Проверка журналов CloudTrail

Просмотрите журналы CloudTrail для событий аутентификации:

bash
# Проверка событий аутентификации AppSync
aws logs filter-log-events \
    --log-group-name /aws/appsync/apis/YOUR_API_ID \
    --filter-pattern '{ $.eventType = "AUTHORIZE" }' \
    --output table

Шаг 4: Использование отладки AppSync

Включите отладку AppSync в ваших резолверах для просмотра точного потока авторизации:

javascript
// Добавление отладки в шаблон резолвера
#set($context.stash.debug = "Authorization check: $context.identity")
#if($context.identity.apiKey)
    #set($context.stash.authMethod = "API_KEY")
#elseif($context.identity.claims)
    #set($context.stash.authMethod = "OIDC")
#end
{
    "version": "2018-05-29",
    "operation": "Invoke",
    "payload": {
        "debug": "$context.stash"
    }
}

Источники

  1. Руководство разработчика AWS AppSync - Авторизация
  2. Ссылка на директивы схемы AWS AppSync
  3. Резолверы AWS AppSync - Работа с идентификатором
  4. Лучшие практики AWS AppSync - Аутентификация и авторизация
  5. Интеграция AWS API Gateway с AppSync

Заключение

Конфликты авторизации в AWS AppSync возникают потому, что сервис не предназначен для одновременной оценки нескольких методов авторизации в одном конвейере резолверов. Чтобы правильно поддержать как OPENID_CONNECT, так и API_KEY авторизацию, вы должны:

  1. Разделять методы авторизации, создавая отдельные резолверы или операции GraphQL для каждого типа аутентификации
  2. Использовать API Gateway как слой авторизации для обработки аутентификации до того, как запросы достигнут AppSync
  3. Реализовать условную логику в резолверах для обработки разных сценариев аутентификации
  4. Рассмотреть возможность использования нескольких API AppSync, если вам нужны полностью отдельные среды авторизации

Проблема с null-идентификатором возникает потому, что система авторизации AppSync не может успешно проверять запросы при наличии нескольких конфликтующих директив авторизации. Реализуя разделение ответственности и правильное分层ирование авторизации, вы можете поддерживать как функциональность API_KEY, так и OPENID_CONNECT без конфликтов.

Авторы
Проверено модерацией
Модерация