Заголовок авторизации не отправляется / не просматривается в облачных функциях с использованием Fetch - PullRequest
0 голосов
/ 30 октября 2019

Похоже, я бьюсь головой об стену ... Я пытался разными способами получить простой вызов Fetch для отправки заголовка авторизации. Вы можете увидеть демо на https://demo -gcf-auth.netlify.com / .

Я получил ряд различных ошибок CORS, но в конечном итоге все они происходят от любого доступа-Control-Allow-Credentials стирается (становится '') или, проще говоря, в первую очередь отсутствует заголовок авторизации.

Это простая index.html, размещенная на Netlify, с Google CloudФункционирует как бэкэнд, который должен получить токен и затем передать его обратно. Конечно, это началось с гораздо более сложной настройки, но я даже не заставляю работать эту крайне минимальную версию. Я также проверил сценарий, используя почти эквивалентный вызов лямбда-функции AWS, которая прекрасно работает. Эта функция имеет автоматически сгенерированный API-шлюз впереди, но я указал некоторые базовые настройки CORS.

Похоже, что шаблон для вызова res.end(), если вызов имеет метод OPTIONS, был предложен ранее,но я не уверен, почему это было бы хорошим способом справиться с этим. Тем не менее, я не получаю возвращенный токен.

В настоящее время заголовок авторизации, похоже, даже не отправляется, а тем более даже получается в бэкэнде.

Кто-нибудь видит, куда это идетнеправильно?

Пожалуйста, не предлагайте использовать пакет npm cors, поскольку это не помогает и не делает ничего, что не может быть явно запрограммировано без этой конкретной зависимости.

Я обнаружил связанную проблемупроисходит в https://community.netlify.com/t/authorization-header-is-undefined/4117.

Бэкэнд облачных функций

'use strict';

exports.minimalAuthorization = function(req, res) {
    const ORIGIN = req.headers.origin;
    console.log('ORIGIN', ORIGIN);

    const TOKEN = req.headers.Authorization || req.headers.authorization;
    console.log('TOKEN', TOKEN);

    const METHOD = req.method;
    console.log('METHOD', METHOD);

    if (req.method === 'OPTIONS') {
        res.end();
    } else {
        res.set('Access-Control-Allow-Origin', ORIGIN);
        res.set('Access-Control-Allow-Credentials', 'true');
        res.set('Access-Control-Allow-Methods', '*'); // POST, OPTIONS
        res.set('Access-Control-Allow-Headers', '*'); // Origin, Content-Type, Accept, Authorization, authorization

        if (TOKEN) {
            res.status(200).send(JSON.stringify(TOKEN));
        } else res.status(400).send(JSON.stringify('Sorry, no token for you...'));
    }
};

Соответствующий раздел HTML-скрипта

<script>
    const ENDPOINT =
        'https://europe-west1-cloud-developer-basics.cloudfunctions.net/minimalAuthorization';
    const TOKEN = `eyJhbGciOiJSUzI1NiIsImtpZCI6ImEwYjQwY2NjYmQ0OWQxNmVkMjg2MGRiNzIyNmQ3NDZiNmZhZmRmYzAiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiTWlrYWVsIFZlc2F2dW9yaSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQUF1RTdtQVhELWtPZHpKbDVDMF9ad3JLY3A3Q2VWWmQzQlp3eG5ydzlicUFTd1UiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY2xvdWQtZGV2ZWxvcGVyLWJhc2ljcy1lZmUzOSIsImF1ZCI6ImNsb3VkLWRldmVsb3Blci1iYXNpY3MtZWZlMzkiLCJhdXRoX3RpbWUiOjE1NzIyOTc0NTksInVzZXJfaWQiOiJwVERCelM0ZDVyWnJqYjlvMFZLa3g3YmtTZnYyIiwic3ViIjoicFREQnpTNGQ1clpyamI5bzBWS2t4N2JrU2Z2MiIsImlhdCI6MTU3MjI5NzQ2MCwiZXhwIjoxNTcyMzAxMDYwLCJlbWFpbCI6Im1pa2FlbHZlc2F2dW9yaUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjEwNzUwNDM4MzUwMTA4NDY5ODEzNCJdLCJlbWFpbCI6WyJtaWthZWx2ZXNhdnVvcmlAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ29vZ2xlLmNvbSJ9fQ.LI2ySD6uafnkruEDDmkym6JKoMdhjOEOGKQiytc3SFyeCDERwylqwsmiaCtE7Q6W_FjqrNaAW2rV09rcvuQFPGAMA8uSGiUCdlwau1tBIENHu_HGdW4wI_PWEi6sRmIpbMPTsIPjpcmsSIcpd_WDtz4EldAboXkottFSS7dU81MDbdgrdwKyaq8y-haJqBtr2LAIHy5rg7leSXyY9wqmj9u4iwExWn-pY6BK7dGCEJFTK0_Czvs3qi-0e8bEPmUXwiKzuuMIL_B9l22EHZXqJv0nd9LIzN5_ofyv63U2rG4DbTgNupRAeibhxUO5djVNtgCcFV49618t9ca81d7znQ`;

    async function callApiWithToken(token) {
        console.log('Calling API with token:', token);

        await fetch(ENDPOINT, {
            method: 'POST',
            credentials: 'include',
            headers: {
                Authorization: `Bearer ${token}`
            }
        })
            .then(res => res.json())
            .catch(error => {
                console.error(error);
            });
    }

    callApiWithToken(TOKEN);
</script>

Полностью функциональный лямбда-эквивалент AWS

'use strict';

function minimalAuthorization(event, context) {
    const TOKEN = event.headers.Authorization.split('Bearer ')[1];
    console.log('TOKEN', TOKEN);

    const ORIGIN = event.headers.origin;
    console.log('ORIGIN', ORIGIN);

    if (TOKEN) {
        return {
            statusCode: 200,
            body: TOKEN,
            headers: {
                'Access-Control-Allow-Origin': ORIGIN,
                'Access-Control-Allow-Credentials': true
            }
        };
    } else
        return {
            statusCode: 400,
            body: 'Sorry, no token for you...',
            headers: {
                'Access-Control-Allow-Origin': ORIGIN,
                'Access-Control-Allow-Credentials': true
            }
        };
}

exports.handler = async (event, context) => {
    return minimalAuthorization(event, context);
};

Конфигурация функции без сервера, AWS Lambda

functions:
  minimalAuthorization:
    handler: functions/minimalAuthorization.handler
    events:
      - http:
          method: GET
          path: minimalAuthorization
          cors:
            origin: 'https://demo-gcf-auth.netlify.com'
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
            allowCredentials: true

Ответы [ 2 ]

0 голосов
/ 31 октября 2019

Ух ты, это должно было быть намного более очевидным. В разные моменты я видел рекомендацию всегда явно устанавливать Content-Type (всегда используя значение application/json). Это было полностью ложно, и должно быть удалено, или установите Тип содержимого, например, text/plain. Это задокументировано на http://50linesofco.de/post/2017-03-06-cors-a-guided-tour#credentials-and-cors.

Не совсем понятно, почему тот же самый фактический HTML обеспечил бы функционирующий ответ 204 + 200 с AWS Lambda, хотя ...

Закрытие. Если вы застряли в CORS, я предоставлю примеры внешнего и внутреннего интерфейсов, чтобы сделать эту работу для вас, если вам потребуются заголовки авторизации.

Frontent

// CORS mode is default, as is Content-Type: 'text/plain'; both are required
await fetch(ENDPOINT, {
    credentials: 'include',
    headers: {
        Authorization: `Bearer ${token}`
    }
})
    .then(res => res.text())
    .then(data => console.log(data))
    .catch(error => {
        console.error(error);
    });

Backend

exports.minimalAuthorization = function(req, res) {
    const TOKEN = req.headers.authorization;
    console.log('TOKEN', TOKEN);

    const METHOD = req.method;
    console.log('METHOD', METHOD);

    res.set('Access-Control-Allow-Origin', 'https://demo-gcf-auth.netlify.com'); // Your origin here
    res.set('Access-Control-Allow-Credentials', 'true');
    res.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');

    // A 204 response (preflight response) happens before actually trying to respond, so accept the auth header and send an OK
    if (METHOD === 'OPTIONS') {
        res.set('Access-Control-Allow-Headers', 'Authorization');
        res.status(204).send('');
    } else {
        // If it's not an OPTIONS request, actually do send the value/token back
        if (TOKEN) {
            res.status(200).send(JSON.stringify(TOKEN));
        } else res.status(400).send(JSON.stringify('Sorry, no token for you...'));
    }
};

0 голосов
/ 31 октября 2019

Я немного сбит с толку относительно того, почему бэкэнд-код устанавливает источник в заголовок источника запроса. Думаю, это для целей тестирования, но это будет означать, что все запросы действительны.

const ORIGIN = req.headers.origin;

Это будет то же самое, что и настройка

const ORIGIN = '*';

Однако мне удалось заставить ваш код работать снесколько твиков:

'use strict';

exports.minimalAuthorization = function(req, res) {
    const ORIGIN = req.headers.origin;
    console.log('ORIGIN', ORIGIN);

    const TOKEN = req.headers.authorization;
    console.log('TOKEN', TOKEN);

    const METHOD = req.method;
    console.log('METHOD', METHOD);

    res.set('Access-Control-Allow-Origin', ORIGIN);
    res.set('Access-Control-Allow-Credentials', 'true');
    res.set('Access-Control-Allow-Methods', '*'); // POST, OPTIONS

    if (req.method === 'OPTIONS') {
        res.set('Access-Control-Allow-Headers', 'Authorization');
        res.status(204).send('');
    } else {
        if (TOKEN) {
            res.status(200).send(JSON.stringify(TOKEN));
        } else res.status(400).send(JSON.stringify('Sorry, no token for you...'));
    }
};

Я думаю, что метод OPTION должен возвращать ответ 2xx, чтобы пройти предпечатную проверку.

Также я переместил некоторые заголовки, такие как «Access-Control-Allow-Origin», из ответа не OPTIONS и поместил «Access-Control-Allow-Headers» в ответ OPTIONS.

Я также создал интерфейс на codepen для проверки функции: https://codepen.io/baktakt/pen/KKKyKzp?editors=0011

...