Pull to refresh

OAuth2.0 авторизация в Vk средствами Qt5.8 и выше

Reading time5 min
Views15K

Вступление

Qt5.8 принёс нам QNetworkAuthorization -- модуль авторизации на сторонних сервисах. Пока что поддерживаются только протоколы OAuth и OAuth2.0, но обещали позже подвезти и OpenID.

В стандартной документации лежат два примера: для реддита и твиттера, также нашёлся пример работы с гуглом. Но т.к. Vk нам всем ближе, было решено запилить статью и для него.

OAuth2.0

Я не стану вдаваться в подробности протокола, за меня это уже неплохо сделали здесьздесь и вот здесь.

В своей статье я лишь кратко расставлю по местам действующие лица, которые понадобятся при написании кода:

  • Client -- программа, которую вы пишете, и которой очень нужно получить данные пользователя или действовать от его имени.

  • Resource owner -- он же user, т.е. владелец прав, аккаунтов и т.д.

  • Authorization server -- сущность, которая, после успешной авторизации, выдаст client-у токен, позволяющий ему в течение некоторого времени работать от имени user-а.

  • Resource server -- сервер с защищёнными данными (собственно, сервер Vk, на который будут отправляться запросы).

Установка QNetworkAuthorization

  1. Включаем vpn (без него сейчас Qt никак не пошевелить), я использую Windscribe или Tunnelbear.

  2. Устанавливаем модуль. Я использую Qt6.4.0-beta.

  3. Также советую установить OpenSsl (в самом низу списка), чтобы потом не ставить его отдельно для libcryptho.dll. Если у вас в системе уже где-то есть OpenSsl, то эту галочку можно не прожимать.

Создание приложения Vk (client-а)

Чтобы Client мог обращаться к authorization server-у, ему нужны client_id и client_secret.

  • client_id -- это уникальный публичный идентификатор клиента.

  • client_secret -- ключ, который должен быть полностью секьюрен. Позволяет подтвердить, что под этим client_id не скрывается некто иной, например, фишинговый сайт мошенника.

Можно рассматривать client_id как логин вашего приложения на сервере авторизации, а client_secret -- как пароль.

Для начала нужно перейти на страницу менеджмента приложений и авторизоваться на ней.

Нажимаем кнопку “Создать”.

Выбираем Standalone-приложение и вводим название:

Тыкаем «Подключить приложение», вводим код из смс и переходим на вкладку «Настройки».

Для начала нужно запомнить два поля: «ID приложения» (client_id), и «Защищённый ключ» (client_secret). Нажимаем на иконку глаза справа от звёздочек, вводим код и перезагружаем страницу. Также нужно сменить настройку «Состояние» на «Приложение включено и видно всем».

Теперь нужно настроить параметры OAuth. Для этого включаем Open API, это позволит использовать Authorization Code Flow.

Если у вас есть один или несколько IP, через который будет проводиться авторизация пользователей, то эти IP необходимо ввети во вкладку “Базовый домен”, но т.к. это чисто тестовое приложение, то авторизовываться мы в нём буду сразу с клиента, поэтому в “Базовый домен будет“127.0.0.1”. То же самое введу в поле “Адрес сайта” в качестве затычки.

Жмём “Сохранить изменения”, перезагружаем страницу и в появившемся поле “Доверенный redirect uri” вводим адрес, на который будет отправляться запрос с кодом, по которому позже можно будет взять токен у authorization server. В этом приложении весь путь авторизации идёт на клиенте, а потому вводим “http://127.0.0.1:6543/”. Сохраняем ещё раз, и можно закрывать страницу.

Собственно, код

Запускаем IDE, создаём “Приложение Qt Widgets”. На Qt5 лучше брать связку из MinGW + qmake. Для Qt6 лучше подходит msvc + CMake.

Добавляем в CMakeList.txt:
find_package(Qt6 REQUIRED COMPONENTS NetworkAuth)
target_link_libraries(VkOAuth2Project PRIVATE Qt6::NetworkAuth)

Если у вас qmake, достаточно написать в pro-файле
QT += networkauth

Далее возьмём из документации Vk некоторые ссылки и значения:

    const QUrl authUrl{ "https://oauth.vk.com/authorize" };
    const QUrl tokenUrl{ "https://oauth.vk.com/access_token" };
    const QString clientSecret{ "d4rZuR8zHiYHgntXC1kp" };
    const QString clientId{ "51400815" };
    constexpr quint32 scopeMask = 2;//https://dev.vk.com/reference/access-rights

Этап 1. Начальная настройка

Для начала нужно создать объекты QOAuth2AuthorizationCodeFlow и QOAuthHttpServerReplyHandler. Первый отвечает за весь цикл авторизации и последующие запросы, второй нужен, чтобы принять code и token от authorization server-a и создать redirect_uri.

    auto oauth = new QOAuth2AuthorizationCodeFlow(&mainWindow);
    auto replyHandler = new QOAuthHttpServerReplyHandler(6543, &mainWindow);

Обращаю внимание, что порт у replyHandler такой же, как в настройках приложения на сайте Vk. Это важно, т.к. этот объект вернёт redirect_uri формата http://127.0.0.1:<port>/. Это поведение можно изменить, переопределив в потомке QOAuthHttpServerReplyHandler виртуальный метод callback.

Далее заполняем oauth настройками:

    oauth->setReplyHandler(replyHandler);
    oauth->setAccessTokenUrl(tokenUrl);
    oauth->setAuthorizationUrl(authUrl);
    oauth->setClientIdentifier(clientId);
    oauth->setClientIdentifierSharedKey(clientSecret);
    oauth->setScope(QString::number(scopeMask));

Этап 2. Подтверждение доверия клиенту от пользователя

Resource owner должен подтвердить доверие client-у, после чего authorization server даст client-у одноразовый code. Для этого есть два пути.

QObject::connect(oauth, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, &QDesktopServices::openUrl);

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

Текст можно переопределить с помощью метода QOAuthHttpServerReplyHandler::setCallbackText. Можно даже без проблем возвращать html-страницу.

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

При этом стоит учитывать, что модуль QWebEngine не работает с компилятором mingw (по крайней мере, так было, когда я тестил), да и сам модуль довольно тяжёлый, так что тянуть его ради одной только авторизации может быть неразумно.

Этап 3. Получение token из одноразового code

Здесь нам ничего делать не нужно, Qt сам обратится по адресу, переданному ему в tokenUrl за токеном, и уведомит нас об окончании процесса авторизации.

Что можно сделать после авторизации

После авторизации client полностью вправе действовать от лица пользователя в рамках установленного scope (набора прав). Мы установили scope = 2, что по документации означает «доступ к списку друзей». Поэтому мы может запросить у сервера Vk список друзей какого-либо пользователя.

//Сигнал granted излучается при успешной авторизации
QObject::connect(oauth, &QOAuth2AuthorizationCodeFlow::granted, [oauth]() {
        const QUrl getFriends{ "https://api.vk.com/method/friends.get" };
        
        //Vk требует указывать версию API в параметрах запроса. Укажем 5.131
        auto network_reply = oauth->post(getFriends, { { "v", 5.131 } });
        QObject::connect(network_reply, &QNetworkReply::finished, [network_reply] {
            //Ответ будет удалён позже, когда отработают все связанные 
            //с ним сигнально-слотовые соединения, 
            //поэтому такое удаление безопасно
            network_reply->deleteLater();

            QJsonDocument response = QJsonDocument::fromJson(network_reply->readAll());
            qDebug() << "All friends ids:";
            for(const auto& user_id : response["response"]["items"].toArray())
                qDebug() << '\t' << user_id.toInteger();
        });
    });

Запустим процесс авторизации:

oauth->grant();

После авторизации в браузере, в консоли можно будет наблюдать список всех друзей пользователя:

Заключение

Развитие модуля на этом не заканчивается. Разработчики пока так и не завезли OpenID, а чтобы авторизовываться через сторонний сервер, вам придётся написать свою реализацию QAbstractOAuthReplyHandler, хотя со введением в Qt6.3 модуля QtHttpServer из QtLabs эта задача значительно облегчается.

Полный код доступен на GitHub

Tags:
Hubs:
Total votes 9: ↑9 and ↓0+9
Comments19

Articles