Или как делегировать авторизацию профессионалам
Введение
Помните времена, когда каждый сайт требовал создать новый аккаунт? Придумать пароль, подтвердить email, запомнить логин... А потом через месяц — кнопка "забыли пароль?". Знакомо?
Сегодня пользователи ожидают увидеть кнопку "Войти через ...". Это удобно, быстро и безопасно. И если вы разработчик, который до сих пор не добавил OAuth в свой проект, эта статья для вас.
Я покажу, как за пару десятков минут интегрировать OAuth 2.0 в ваше приложение на Go.
Немного теории: OAuth 2.0
Прежде чем писать код, давайте быстро разберемся, что такое OAuth 2.0 и почему он вам нужен.
Что такое OAuth 2.0?
OAuth 2.0 — это не протокол аутентификации (определения личности), а протокол авторизации (выдачи прав). Его задача — позволить одному приложению получить ограниченный доступ к данным пользователя на другом сервисе без передачи пароля.
Основные участники процесса
- Resource Owner (Владелец ресурса) — это пользователь
- Client (Клиент) — ваше приложение
- Authorization Server (Сервер авторизации) — Google, Яндекс, VK и т.д.
- Resource Server (Сервер ресурсов) — API, где хранятся данные пользователя
В статье в качестве сервера авторизации я буду использовать Яндекс (почему бы и нет?).
Алгоритм подключения других провайдеров моментами может отличаться, но поняв сам процесс, разобраться в новом провайдере не
составит большого труда.
Как это работает
- Пользователь нажимает "Войти через Яндекс" в вашем приложении
- Ваше приложение перенаправляет пользователя на страницу авторизации Yandex
- Пользователь авторизуется и разрешает доступ к своим данным
- Yandex перенаправляет пользователя обратно в ваше приложение с кодом авторизации
- Ваше приложение обменивает этот код на access token (токен доступа)
- С помощью токена вы запрашиваете данные пользователя из API
Звучит сложно? На практике это всего несколько строк кода!
Пишем код
Шаг 0: Настройка окружения
Установим зависимости:
go get golang.org/x/oauth2
Почему именно эти библиотеки?
- `golang.org/x/oauth2` — официальная библиотека от команды Go для работы с OAuth 2.0
Шаг 1: Регистрация приложения у провайдера
Прежде чем писать код, нужно зарегистрировать ваше приложение у OAuth-провайдера.
Регистрация приложения
- Переходим в Yandex OAuth
- Создаем новое приложение
- Выбираем тип приложения:
Для авторизации пользователей. Далее вам предложат пройти верификацию, этот шаг необязателен, поэтому сейчас мы его пропустим - Заполняем:
- Название вашего сервиса: Любое название (например, "My App OAuth")
- Иконка: Иконка вашего приложения
- Почта: Email, куда провайдер будет использовать для отправки вам различных уведомлений
- Платформы приложений: в нашем случае, Веб-севрисы
- RedirectURI: `http://localhost:8080/auth/yandex/callback`. В будущем мы напишем для него обработчик
- Suggest Hostname: При локальной разработке он нам не нужен, но для использования в продакшене следует указать хост вашего сайта (где будет кнопка авторизации)
- Права доступа к данным пользователей: выбираем данные, которые будут доступны нашему серверу после успешной авторизации. Пользователь, нажимая авторизоваться, будет видеть, что
мы у него запрашиваем. Сейчас выберем базовый минимум - доступ к адресу электронной почты - Видим превью окошка с авторизацей и если все ок, нажимаем "Создать приложение". Получаем поля, которые понадобятся нам в коде:
- Client ID
- Client Secret
Важно: Client Secret — это секретный ключ. Никогда не коммитьте его в публичный репозиторий!
Шаг 2: Настраиваем OAuth в коде
Предполагается, что у вас уже реализован сервер, а в качестве примера я буду использовать HTTP-сервер стандартной библиотеки.
Для работы авторизации базово нам нужно 2 обработчика и oauth2 конфиг, я поместил их в пакет httpapi.
Разберем, что здесь происходит:
OAuth Config: Создаем конфигурацию для провайдера
- `ClientID` и `ClientSecret` — ваши credentials, полученные ранее. Я тяну их из переменных окружения
- `RedirectURL` — куда провайдер вернет пользователя (тот самый RedirectURI, который мы указали ранее)
- `Scopes` — какие данные мы запрашиваем (email, профиль и т.д.)
- `Endpoint` — URL'ы для авторизации и обмена токена
Шаг 3: Реализуем обработчики
Обработчик начала авторизации
Что здесь важно:
- State — это защита от CSRF-атак. Мы генерируем случайную строку, сохраняем ее в cookie и проверяем при возврате
Обработчик callback
Пошагово что происходит:
- Проверяем state — защита от CSRF
- Получаем код из query параметров
- Обмениваем код на токен — здесь происходит магия OAuth
- Запрашиваем данные пользователя из Yandex API
- Возвращаем результат — в реальном приложении здесь создается сессия.
Например, генерируется JWT токен и устанавливается в Cookie. Для простоты, мы просто установим заглушку в Cookie,
наличие которой будет означать, что пользователь авторизован
Главная страница с кнопкой входа
Добавим страницу авторизации и обработчик для ее отображения в `main.go`
Создаем папку `files` и файл `templates/auth.html`:
Приватная страница
Чтобы убедиться, что наш механизм работает, создадим простой обработчик /private, отдающий файл с картинкой, НО только авторизованным пользователям:
Вуаля! Мы сделали простую проверку наличия куки, которую проставляет обработчик успешной авторизации. Отсутствие такой куки
будет означать, что пользователь не был авторизован.
В реальном приложении, конечно, проверка была бы сложнее и происходила бы скорее всего на каком-нибудь Nginx или в middleware слое.
Но это уже тема для отдельной статьи...
Выход и выбор другого аккаунта
У вас может возникнуть закономерный вопрос: если пользователь авторизовался и позже он надумает сменить аккаунт, как же ему быть?
Ответ прост — давайте добавим возможность разлогиниться!
Только вот в случае с Yandex OAuth, все не так просто и API провайдрера не предоставляет отдельного endpoint'а на logout. Поэтому, поступим хитрее:
каждый раз, когда пользователь будет вызывать метод авторизации, будем давать ему возможность выбора аккаунта.
Такое решение никак не мешает уже авторизованному пользователю, так как наше приложение будет понимать, что он авторизован и не
будет вести его на /auth). А вот когда пользователь захочет выйти, удалим наши авторизационные данные и поведем его на повторную авторизацию с
выбором аккаунта.
Для этого в методе YandexAuth добавим опицию, позволяющую указать провайдеру, что мы хотим потребовать от пользователя подтверждение пользователя
И создадим обработчик, удаляющий нашу авторизационную куку и ведущий на страницу /login.
Результат
Теперь мы готовы посмотреть, что получилось:
- Запускаем сервер
- Открываем браузер: `http://localhost:8080/login`
- Нажимаем на кнопку входа и проходим авторизацию. Если все сделано правильно, вы увидите JSON с данными пользователя!
- Проверяем, что картинка по адресу `http://localhost:8080/private` нам доступна
- Выходим: `http://localhost:8080/logout`
- Проверяем, что картинка по адресу `http://localhost:8080/private` больше недоступна
- И мы снова можем авторизоваться через `http://localhost:8080/login`
Теперь, вы знаете как буквально за пару минут добавить авторизацию в свой проект и сделать жизнь пользователей лучше!
В продолжении этой статьи, я планирую рассказать про дальнейшую настройку на стороне сервера, а именно — про использование
JWT для аутентификации пользователя и про проверку авторизационной куки на Nginx.
Полный код ждет вас в репозитории.
Кстати, такую же авторизацию я реализовал для стартапа CVortex в акселераторе от стартап-студии «Структура».
#go #golang #oauth #oauth2 #authentication #authorization #security #tutorial #backend #webdev