TomskDotNet #6 — первый пост-коронавирусный митап и выход .NET 5!

Наконец-то! После почти года перерыва митапы потихоньку возвращаются!

22 октября мы провели очередной — и очень неплохой — митап (фото и видео — традиционно на сайте). Лично я был очень удивлен такому большому количеству смельчаков, не побоявшихся прийти к нам в оффлайн. Следуя коронавирусным традициям, в этот раз у нас была и онлайн часть, и даже одному из «удаленных» участников достался сертификат от Jetbrains — наш приятный подарок за активность и хорошие вопросы.

У меня на этом митапе был небольшой вступительный доклад про грядущий выход .NET 5, а сейчас, спустя месяц, я могу поделиться и собственным опытом переезда на новую версию с 3.1. Мои небольшие пет-проджекты и чуть большая open-source библиотека авторизации переехали за пару вечеров без каких-либо проблем. По сравнению с миграцией 2.2 -> 3.0 (которая, пожалуй, заняла недели 2-3) — это просто сказка. Единственный breaking-change с которым мне пришлось столкнуться — это небольшое изменение в контексте авторизации. В остальном обновление свелось к чистке Startup.cs и изменению версий зависимостей в .csproj. Идеально! :)

Так что если задумываетесь над обновлением — не задумывайтесь :) В сети куча статей про 20% рост производительности, так что причин откладывать нет ну вообще никаких (ну, разве что помните, что .NET 5 — не LTS и обновляться до .NET 6 придется оперативно после его выхода).

Ну и смотрите видеозапись доклада, если интересно :)

OAuth в SPA или неожиданные сложности интеграции логина через соцсети в React с Asp.Net Core

Это история про то, как казалось бы типичная задача интеграции входа через соц.сети в React/Asp.Net Core приложении может превратиться в длинную сагу и закончиться open-source библиотекой :)

Если читать лень, то можно сразу пойти на гитхаб, где и посмотреть весёлую гифку и прочую документацию по интеграции и использованию, ну а здесь я расскажу чуть подробнее :)


В Asp.Net Core существует замечательная встроенная интеграция с внешними провайдерами аутентификации (OAuth/OpenId и прочее нестандартное), а также сторонние плагины, поддерживающие аутентификацию даже через VK. Однако весь этот механизм подразумевает, что у вас обычное server-side приложение (с forms-авторизацией), и никаких собственных access_token’ов, которые привычны в SPA вам генерироваться не будет.

Вот вот мне и загорелось желание воспользоваться всем этим огромным количеством готовых решений и подружить его с SPA. Как схема работы должна выглядеть в идеале? Согласно AuthCode Flow (который считается рекомендуемым для использования в SPA), это должно выглядеть примерно так:

SPA получает AuthCode у стороннего провайдера и передает его на бэкэнд приложения. Бэкэнд проверяет верность кода, ищет этого пользователя в своей БД (и создает, если требуется) и возвращает на фронтенд access_token, с которым и происходят все дальнейшие запросы.

Для первого шага (получения AuthCode) в SPA существуют готовые реализации в виде, например, реакт-компонентов. Но под каждого OAuth-провайдера они разные, и подбор и настройка могут отнять достаточно много времени. После интеграции написанной библиотеки IdentityOAuthSpaExtensions (и настройки бэкэнд-части согласно инструкциям от майкрософта), запрос AuthCode из SPA будет состоять из двух частей:

  1. Создание обработчиков и подписка на события:
  2.     window.addEventListener("message", this.oAuthCodeReceived, false);
        function oAuthCodeReceived(message) {
            if (message.data && message.data.type === 'oauth-result') {
                if (data.code) {
                    externalAuthSuccess(data.provider, data.code);
                } else {
                    externalAuthError(data.provider, data.error, data.errorDescription);
                }
            }
        }
        function externalAuthSuccess(provider, code) {
            alert(`Provider: ${provider}, code: ${code}`);
        }
        function externalAuthError(provider, error, errorDescription) {
              alert(`Provider: ${provider}, error: ${error}, ${errorDescription}`);
        }
    
  3. Старт процедуры авторизации:
   window.open(`${window.location.protocol}//${window.location.hostname}:${window.location.port}/external-auth/challenge?provider=${provider}`, undefined, 'toolbar=no,menubar=no,directories=no,status=no,width=800,height=600');

В результате этого ваше SPA получит AuthCode стороннего провайдера. В дальнейшем с ним можно делать что угодно ( :)), но в нашем случае, мы хотим получить access_token от нашего бэкэнда, чтобы в дальнейшем все http вызовы совершать с этим access_token’ом. Для этого в библиотеке существует возможность проверки AuthCode («), а также (рекомендуемая) интеграция с IdentityServer в виде extension grant’a. Описание интеграции очень подробно описано на гитхабе

Итоговая схема выглядит как-то так:

Из основных плюсов библиотеки:

  1. Единая точка входа и общий интерфейс интеграции любых Auth-провайдеров (не нужно менять SPA, меняется лишь одна переменная — имя провайдера — при открытии URL авторизации)
  2. Использование стороннего кода для взаимодействия с OAuth (саму библиотеку не придется обновлять, если в сторонних OAuth что-то изменится).
  3. Добавление новых провайдеров происходит стандартным способом (по инструкции для server-side приложений) и не требует модификации самой библиотеки

Пользуйтесь, задавайте вопросы и рассказывайте об успешных сценариях внедрения!