Прежде чем я начну, я хочу прояснить, что нижеследующее является доказательством концепции. Это абсолютно не рекомендация, а скорее отправная точка для разговора. В последнее время я много думал о том, как можно использовать OpenWhisk вместе с какой-то моделью безопасности. В частности, «Разоблачить действие такое-то и такое-то, но только для авторизованных пользователей». Очевидно, что «безопасность» может означать гораздо больше, но в этом начальном посте я собираюсь немного упростить свои требования.

  • Аутентифицируйте пользователя как-нибудь.
  • Сделать действие OpenWhisk Foo обязательным пользователем, вошедшим в систему.

Для аутентификации я прожевал несколько разных вещей. Теоретически я мог настроить базу данных Cloudant и создать действия для типичной процедуры регистрации/входа пользователя, но мне очень-очень не хотелось этого делать. Вместо этого я решил, наконец, взглянуть на Auth0. Я слышал об этом сервисе раньше, но никогда не видел возможности поиграть с ним. Оказалось, это было довольно легко. (У меня есть еще несколько комментариев по поводу Auth0, но я оставлю их в конце поста.) Я настроил новое приложение Auth0, используя их настройку Lock на стороне клиента для обработки аутентификации. Я создал невероятно плохую демо-версию на стороне клиента без реального пользовательского интерфейса и — да — я сказал, что это было плохо? Передняя часть состоит из двух кнопок:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width">
    </head>
    <body>

        <p>
        <button id="loginBtn">Login</button>
        </p>

        <p>
        <button id="testFooBtn">Test Foo</button>
        </p>
        
        <script src="https://code.jquery.com/jquery-3.1.1.min.js"   integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="   crossorigin="anonymous"></script>
        <script src="https://cdn.auth0.com/js/lock/10.14/lock.min.js"></script>
        <script src="app.js"></script>
    </body>
</html>

Я включаю код Auth0 внизу вместе с jQuery. Мои две кнопки управляют входом в систему, а также вызовом действия, которое я продемонстрирую через минуту. Код JavaScript в основном взят из кода Auth0. Опять же, я хочу прояснить, что это всего лишь примерный код, позволяющий мне попробовать.

let lock;
let idToken;

$(document).ready( () => {
    console.log('lets do this');

    lock = new Auth0Lock('asKI36rzsbOyY40DaGv6mPWODbexIO-R','raymondcamden.auth0.com');

    $('#loginBtn').on('click',doLogin);

    $('#testFooBtn').on('click',doTestFoo);

    idToken = localStorage.getItem('id_token');
    if(idToken) {
        getProfile();
    }

    lock.on("authenticated", function(authResult) {
        console.dir(authResult);
        localStorage.setItem('id_token', authResult.idToken);
        idToken = authResult.idToken;
        getProfile();
    });

});

function doLogin() {
    lock.show();
}

function getProfile() {
    lock.getProfile(idToken, function(error, profile) {
        if (error) {
            // Handle error
            return;
        }
        // Display user information
        //show_profile_info(profile);
        console.dir(profile);
    });
}

Когда нажимается кнопка входа, я запускаю метод show Lock API. Вот где Auth0 действительно хорош. Он полностью обрабатывает процесс аутентификации и, в конце концов, оставляет мне JWT (веб-токен JSON), который я могу использовать для аутентификации моих последующих вызовов. Вызов getProfile взят из примера кода Auth0. Я на самом деле не использую его для чего-то практического. Позвольте мне показать вам, как это все выглядит.

Во-первых, страница, которая загружается по умолчанию (и опять же, это не должно быть готовым к работе):

А вот как выглядит процедура аутентификации Auth0:

Между прочим, Facebook и Google были выбраны произвольно — вы можете настроить это (опять же, я расскажу больше об Auth0 в конце).

После входа в систему я возвращаюсь на свою веб-страницу и имею доступ к моему профилю, но важным моментом является значение id_token. Это представляет значение JWT. Я могу использовать это в своем действии OpenWhisk для аутентификации запроса.

Проверка JWT довольно проста. Вот действие, которое я построил:

/*
hard coded secret
*/
const secret = require('./creds.json');
const jwt = require('jsonwebtoken');

/*
args.token, passed in
*/
function main(args) {

    return new Promise( (resolve, reject) => {

        let decoded = jwt.verify(args.token, secret.key, (err,decoded) => {

            if(err) {
                console.log('err',err);
                reject({
                    name:err.name,
                    message:err.message,
                    stack:err.stack
                });
            } else {
                resolve({decoded:decoded});
            }

        });

    });

}

exports.main = main;

Для действия требуется два значения: ключ, специфичный для моего приложения и загружаемый через файл JSON, и сам JWT, который передается. Мне нужен пакет jsonwebtoken, в котором есть метод verify, который я могу запустить для проверки токен действителен как для моего приложения (в зависимости от значения секрета), так и в течение определенного периода времени. И это все. Серьезно - красиво и просто, правда? Я назвал это действие jwtverify.

Итак, чтобы свести это воедино, мне нужно создать действие, которое будет обеспечено. Я создаю следующее очень простое действие с именем foo.

function main(args) {

    if(!args.name) args.name = 'Nameless';
    return {
        result:"Hello "+args.name
    }

}

Я подтолкнул это к OpenWhisk. Теперь у меня есть общее действие «jwt verify» и случайное действие, которое я хочу защитить. Как мне это сделать? С последовательностью. Я создал новое действие под названием secureFoo, основанное на последовательности: jwtverify+foo. (Технически я сделал все это в пакете под названием secblog просто для того, чтобы немного организовать демонстрацию.) Конечным результатом является действие, которое действует как приложение Express с промежуточным программным обеспечением.

Для тестирования я выставил secureFoo через REST API (не как веб-экшен, потому что CORS пока не является простым дополнением). Затем я создал эту функцию для поддержки этой второй кнопки в моем демонстрационном приложении:

function doTestFoo() {
    $.get('https://3b1fd5b1-e8cc-4871-a7d8-cc599e3ef852-gws.api-gw.mybluemix.net/auth1/foo?token='+idToken+'&name=Ray').then((res) => {
        console.log(res);
    });
}

URL-адрес, который вы видите, — это путь, который я указал для своего REST API. Решающий бит находится в конце, где я передаю токен. Это передается первому действию в последовательности, которое проверяет его, а затем переходит ко второму. В результате вы ожидаете:

Вау! Хорошо, готово… вроде. Вы заметили, что мое действие Foo приняло аргумент? Я передал name=Ray в URL, но это не отразилось на результате. Одна важная вещь, которую следует помнить о последовательностях, заключается в том, что аргументы, отправленные в качестве входных данных, относятся только к первому действию. Если вы хотите, чтобы любое последующее действие работало с вводом, оно должно быть передано как результат каждого предыдущего действия.

Поскольку наше первое действие — это обычная вещь типа «защитить этот процесс», один подход, который мы могли бы использовать, — это просто передать все, что было отправлено, кроме исходного токена. Я изменил свой проверочный код следующим образом:

delete args.token; 
resolve(args);

И просто так — это работает. Я не буду делать новый снимок экрана, так как он буквально только что изменился с Nameless на Ray. Одна проблема с этой конкретной настройкой заключается в том, что «токен» теперь, по сути, является зарезервированным словом. Поскольку это мое приложение, и я создаю API, я могу с этим справиться.

Итак… вот и все. Как я уже сказал, я определенно не уверен, насколько это имеет смысл, но я хотел бы услышать мнение людей. Оставьте мне комментарий ниже! Вы можете найти исходный код всего, что я показал здесь: https://github.com/cfjedimaster/Serverless-Examples/tree/master/auth1

Остановить чтение

Или нет. ;) Как я уже сказал выше, Auth0 чертовски хорош. Все в них меня просто впечатлило. Зарегистрироваться было легко. У них была дерьмовая тонна образцов, я имею в виду, черт возьми, почти ошеломляющих с точки зрения примера кода. Одна вещь, которая мне особенно чертовски понравилась, это то, что вы можете протестировать вход через социальные сети, и они будут использовать свои собственные приложения, пока вы не укажете свое собственное. Я имею в виду, что обычно для входа через социальные сети вы заходите на сайт, создаете новое приложение, записываете различные идентификаторы, возвращаетесь к своему коду, копируете их и т. д. и т. д. Это, конечно, не сложно, но скучно. . С Auth0, конечно, только при тестировании, вы можете просто использовать их приложения. Я действительно копаю это! В любом случае, я надеюсь еще много поиграть с Auth0 в будущем, и пока что я определенно могу его рекомендовать!

Первоначально опубликовано на сайте www.raymondcamden.com 17 апреля 2017 г.