/auth-api

API сервиса аутентификации и авторизации в приложение Твой ФФ!

Primary LanguagePythonBSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

auth-api

API сервиса аутентификации и авторизации в приложение Твой ФФ!

Схема работы Auth APUI

Auth Schema

Функционал

  1. Аутентификация и авторизация пользователей
  2. Управление доступами к ресурсам Твой ФФ
  3. Отправка пользовательских данных в Userdata API

Запуск

  1. Перейдите в папку проекта

  2. Создайте виртуальное окружение командой и активируйте его:

foo@bar:~$ python3 -m venv venv
foo@bar:~$ source ./venv/bin/activate  # На MacOS и Linux
foo@bar:~$ venv\Scripts\activate  # На Windows
  1. Установите библиотеки
foo@bar:~$ pip install -r requirements.txt
  • Бэкенд может запуститься без кластера Kafka. Просто не указывайте KAFKA_DSN в .env. Если вам нужна его логика, то тут находится инструкция по поднятию: https://github.com/profcomff/db-kafka
  1. Запускайте приложение!
foo@bar:~$ python -m auth_backend start

ENV-file description

  • DB_DSN – Адрес базы данных в фаормате postgresql://admin:admin@localhost:5432/dev
  • EMAIL – Адрес электронной почты (логин для входа) для отправки уведомлений по Email
  • EMAIL_PASS – Пароль от электронной почты
  • HOST – Хост для использования в шаблонах сообщений электронной почты
  • KAFKA_DSN - Адрес Kafka Cluster
  • KAFKA_USER_LOGIN_TOPIC_NAME - имя топика, куда Auth API пишет пользовательские данные при успешной авторизации
  • KAFKA_TIMEOUT - время, в течение которого kafka worker ждет пока новое сообщение отправится(не используется)
  • KAFKA_LOGIN - Логин в брокер сообщений Kafka
  • KAFKA_PASSWORD - Пароль в брокер сообщений Kafka
  • ENABLED_AUTH_METHODS - включенные методы авторизации
  • TOKEN_LENGTH - длина отдаваемого токена при авторизации
  • SESSION_TIME_IN_DAYS - время, через которое протухнет токен
  • MAX_RETRIES - максимальное кол-во ретраев при отправке письма
  • STOP_MAX_DELAY - максимальная задержка между попытками отправить письмо
  • WAIT_MIN, WAIT_MAX - минимальное и максимальное время ожидания успешной отправки письма
  • EMAIL_DELAY_TIME_IN_MINUTES - окно учёта писем
  • EMAIL_DELAY_COUNT - сколько писем можно отправить максимум в промежутке времени EMAIL_DELAY_TIME_IN_MINUTES

Остальные параметры указаны тут

Google

  • GOOGLE_REDIRECT_URL: str – URL адрес страницы для получения данных авторизации на нашем фронтэнде
  • GOOGLE_SCOPES: list[str] – Запрашиваемые у гугла права на управление аккаунтом, по умолчанию запрашивает данные пользователя
  • GOOGLE_CREDENTIALS: Json – Данные приложения Google, получить можно в Google Cloud Console

Physics

  • PHYSICS_REDIRECT_URL: str – см. секцию Google
  • PHYSICS_SCOPES: list[str] – см. секцию Google
  • PHYSICS_CREDENTIALS: Json – см. секцию Google

LK MSU

  • LKMSU_REDIRECT_URL – URL адрес страницы для получения данных авторизации на нашем фронтэнде

Yandex

  • YANDEX_REDIRECT_URL – URL адрес страницы для получения данных авторизации на нашем фронтэнде
  • YANDEX_CLIENT_ID - ID приложения, созданного в Яндексе
  • YANDEX_CLIENT_SECRET - Ключ для получения токена пользователя в Яндексе

MYMSU

  • MYMSU_REDIRECT_URL – см. секцию Yandex
  • MYMSU_CLIENT_ID - см. секцию Yandex
  • MYMSU_CLIENT_SECRET - см. секцию Yandex

Telegram

  • TELEGRAM_REDIRECT_URL – URL адрес страницы для получения данных авторизации на нашем фронтэнде
  • TELEGRAM_BOT_TOKEN - Токен бота приложения

Основные абстракции

  1. Пользователь - запись в таблице user, может иметь много методов входа, например, VK, email, LKMSU
  2. Скоуп - право на определенное действие над определенным ресурсом. Например timetable.event.update - право на изменение расписания в Timetable API
  3. Группа - запись в таблице Group. Может иметь родителя, в итоге группы образуют дерево(деревья). Группа имеет свои скоупы. Группы также косвенно имеет все скоупы своего родителя, которая в свою очередь имеет скоупы своего родителя и т.д.
  4. Сессия - сущность опредляющая доступ к аккаунту. Имеет токен, свои скоупы. Пользователь авторизуется и тем самым создает сессию. В нее можно поместить те права, которые есть у пользователя. Сессия заканчивается через какое то время и доступ становится невозможен, надо создать новую сессию.

Сценарий использования

Email: регистрация нового аккаунта

  1. Дернуть ручку POST /email/registrate . Вы передаете
{
 "email": "string", // Почта
 "password": "string" // Пароль
}
  1. На почту приходит письмо с линком на GET /email/approve?token='...', если по ней перейти то почта будет подтверждена и регистрацию можно считать завершенной.

Email: вход в аккаунт

  1. Дернуть ручку POST /email/login. там всего один вариант логина, никуда не денетесь
  2. В теле запроса есть scopes, это обязательный параметр. scopes - это лист айдишников, существующих в группах в которых вы состоите прямо или в группах, которые являются родителями ваших групп. Он может быть равен [], если вам не нужны права для этого токена.
  3. Вам придет токен, сохраняйте его куда нибудь, срок действия ограничен.

Email: Восстановление забытого пароля

  1. Дернуть ручку POST /email/reset/password/restore. Вы передаете
{"email": "string" // Почта}
  1. Вам придет письмо, где будет ссылка НА ФРОНТ(надо сделать это), в ссылке будет reset_token
  2. Токен надо передать в ручку POST /email/reset/password в заголовках, вместе с
{"new_password": ""}

и пароль будет изменен

Email: Изменение пароля

  1. Если пароль не забыт, а просто надо его поменять. Тогда в POST /email/reset/password/request передается токен авторизации, в теле вы передаете
{
"password": "string", // старый пароль
"new_password": "string" // новый пароль
}
  1. Отправляете запрос и всё, пароль изменен, вам придет письмо с уведомлением о смене пароляю

Email: Изменение адреса электронной почты

  1. Дернуть ручку POST /email/reset/email/request. Всего один вариант, передаете новое мыло в теле
{"email": "string" //Почта}

и токен атворизации в заголовках 2. На почту придет письмо с подтверждением почты, там будет токен подтверждения в query параметрах. Ссылка ведет на ручку GET пока что, но надо переделать, чтобы тоже вела на фронт.

Google/Physics: вход пользователя с аккаунтом Google

Все примеры написаны для Google аккаунта, для аккаунта physics.msu.ru средует делать запросы к /physics-msu вместо /google

  1. Получаем адрес для запроса на сервер Google: GET /google/auth_url
  2. Редиректим пользователя на этот url, пользователь входит в аккаунт и возвращается на страницу, которую можно узнать запросом GET /google/redirect_url
  3. Если Google не передал в ответе GET параметр error, передаем GET параметры страницы на сервер авториации в теле POST запроса в формате JSON: POST /google/login. Иначе возвращаем ошибку авторизации
  4. При успешном входе получаем token сессии. В теле запроса есть scopes, это обязательный параметр. scopes - это лист айдишников, существующих в группах в которых вы состоите прямо или в группах, которые являются родителями ваших групп. Он может быть равен [], если вам не нужны права для этого токена. Если сервер авторизации ответил ошибкой 401:
    1. запоминаем значение id_token из ответа.
    2. Предлагаем пользователю завести новый аккаунт нашего приложения, связанный с гуглом
  5. Если пользователь соглашается, делаем запрос с {"id_token": "<id-token>"} в теле на адрес POST /google/register. При успешном входе получаем token сессии, иначе показываем экран ошибки авторизации. В теле запроса есть scopes, это обязательный параметр. scopes - это лист айдишников, существующих в группах в которых вы состоите прямо или в группах, которые являются родителями ваших групп. Он может быть равен [], если вам не нужны права для этого токена.

Google/Physics: добавление аккаунта Google как второго метода входа

Все примеры написаны для Google аккаунта, для аккаунта physics.msu.ru средует делать запросы к /physics-msu вместо /google

  1. Получаем адрес для запроса на сервер Google: GET /google/auth_url
  2. Редиректим пользователя на этот url, пользователь входит в аккаунт и возвращается на страницу, которую можно узнать запросом GET /google/redirect_url
  3. Если Google не передал в ответе GET параметр error, передаем данные на сервер авториации: POST /google/register, указываем заголовок Authorization: <auth-token>. Иначе возвращаем ошибку авторизации
  4. При успешном входе получаем token сессии, иначе показываем экран ошибки авторизации

Telegram: добавление аккаунта Telegram как второго метода входа

  1. Получаем адрес обычным способом, перенаправляем туда пользователя
  2. После выдачи доступа пользователь попадает на redirect url с параметром #tgAuthResult=...
    1. На этой странице должен быть встроен виджет авторизации телеграмма, который преобразует #tgAuthResult=... в ?first_name=...&user_name=...
  3. Отослать данные из GET параметров в виде словаря на ручку GET /telegram/login
  4. При успешном входе получаем token сессии, иначе показываем экран ошибки авторизации

Созданиие группы

  1. Дернуть ручку POST /group, передать
{
"name": "string", // имя группы
"parent_id": "string", // айди предка группы(из него подтянутся скоупы)
"scopes": "string" // скуопы группы
}

Итоговые права участников группы будут равны сумме прав участников рожительской группы и прав текущей группы

Создание скоупсов

  1. У вас должен быть аккаунт с правом на создание скоупсов :)
  2. Получаете токен со скоупом auth.scope.create
  3. Идете с токеном на ручку POST /scope, отправляете его туда в заголовке и в теле отправляете
{
"name": "string", // имя скоупа(там должно быть две точки и три слова без пробелов)
"comment": "string" // комментарий
}
  1. Профит

Добавление скоупсов в группу

  1. Дернуть PATCH /group/{id}, передать
{"scopes": ["<scopes>"] // список, содержащий старые скоупы + новые. Ручка полностью заменит скоупы на переданные}

Узнать информацию о себе

  1. Дернуть ручку GET /me, передать в заголовках токен авторизации Ответ содержит поля
  • auth_methods - список доступных методов авторизации пользователя
  • session_scopes - скоупы текущей сессии
  • user_scopes - список доступных пользователю скоупов
  • indirect_groups - список групп в которых пользователь состоит косвенно(то есть родители групп, в которых он состоит непосредственно)
  • groups - список групп в которых пользователь состоит непосредственно
  • email - электронная почта пользователя
  • id - айди в Auth API

Добавление метода аутентификации

  1. Продумайте, какой путь должен совершить пользователь, чтобы войти в сервис с использованием вашего метода аутентификации

    • Все методы должны поддерживать минимум 2 варианта взаимодействия: регистрация нового пользователя (она же, добавление метода аутентификации существующему пользователю) и повторный вход.
    • Большинство внешних приложений (Google/Yandex/Telegram и др.) уже придумали все за вас и используют стандарт OAuth2 для авторизации внешних приложений, поэтому они очень похожи друг на друга и можно посомтреть примеры. Google авторизация уже реализована и можно почитать пути пользователя выше.
  2. Определитесь, какие методы нужны для работы с вашим методом авторизации.

    • По умолчанию есть 2 API ручки: /login – вход (повторный), и /register – первичная регистрация/добавление нового метода авторизации
    • Для OAuth2 авторизации и аутентификации также обязательно определены ручки /auth_url и /redirect_url – возвращают URL, куда пользователя должен перенаправить наш фронтенд для ввода логина и пароля на внешнем ресурсе, и URL, куда внешнее приложение перенаправит результат входа, соответственно
    • Вы можете определить и свои методы, но помните, что их нужно также поддержать и на фронтенде приложения. Обязательно опишите пошагово (а лучше нарисуйте схему в Miro или draw.io), как будут рабоать ваши методы со стороны пользователя/фронтенда
  3. Создайте новый файл в папке auth_backend/auth_plugins, создайте класс и отнаследуйте его

  4. Задайте классу описание, prefix и tags

    """Вход в приложение по аккаунту гугл"""
    prefix = '/google'
    tags = ['Google']

    • prefix используется как отправная точка для ваших методов. Ручка логина для метода авторизации с премиксом /myauth будет /myauth/login
    • Описание и теги используются для документирования кода. Зачастую без них непонятно, что вообще происходит. Не пропускайте их.
  5. Создайте основные методы

    • Помните, что все методы являются @staticmethod или @classmethod. То есть не принимают аргумент self (текущий объект), а принимают ничего или cls (текущий класс) соответственно
    • Ручки /login и /register имеют сигнатуры async def _login(...) и async def _register(...) соответственно
    • Ручка /login обязательно возвращает объект
      class UserSession(BaseDbModel):
    • Ручки /auth_url и /redirect_url методов OAuth2 обязательно возвращают оъект
      class UrlSchema(Base):
      url: str

Contributing

  • Основная информация по разработке наших приложений

  • Основная информация по разработке Auth API тут и тут