Skip to content

About

minimum backend on FastAPI with its own authentication logic (JWT) and authorization through roles/permissions. No built-in auth subsystems of the framework are used.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

Custom Auth & ACL Demo

Минимальный backend на FastAPI с собственной логикой аутентификации (JWT) и авторизации через роли/разрешения. Никаких встроенных auth подсистем фреймворка не используется.

Стек

  • Python 3.11+
  • FastAPI + Uvicorn (только HTTP-обвязка)
  • SQLAlchemy 2.0 (ORM + модель БД)
  • PyJWT + bcrypt (хеширование и выпуск токенов)
  • SQLite по умолчанию, Postgres по DATABASE_URL
  • uv для управления зависимостями (замена requirements.txt)
  • BCRYPT_MAX_PASSWORD_LENGTH=0 — отключает ограничение bcrypt 72 байта (нужно из-за проверки passlib на старте)
  • bcrypt pinned 3.2.x (passlib 1.7.4 не дружит с 4.x)

Как запустить локально (uv)

  1. Установить uv, если нет: curl -LsSf https://astral.sh/uv/install.sh | sh (добавьте ~/.local/bin в PATH).
  2. Установить зависимости: uv sync.
  3. Запуск: BCRYPT_MAX_PASSWORD_LENGTH=0 uv run uvicorn app.main:app --reload (порт 8000).

Переменные окружения:

  • DATABASE_URL — например postgresql+psycopg2://user:pass@localhost:5432/auth (по умолчанию sqlite:///./auth.db).
  • JWT_SECRET_KEY — ключ для подписи токенов.
  • ACCESS_TOKEN_EXPIRE_MINUTES — время жизни access token.

Docker

docker compose up --build
# приложение на http://localhost:8000, Postgres внутри сети docker

Dockerfile использует uv внутри контейнера, psycopg2 собирается через libpq-dev. Env BCRYPT_MAX_PASSWORD_LENGTH=0 выставлен в контейнере.

На старте создаются таблицы и сиды:

  • Пользователь: admin@example.com / Admin1234
  • Роли: admin, viewer, editor
  • Разрешения: (iam, manage), (objects, read), (objects, write)

Схема доступа (RBAC)

  • users — учетные записи (имя/фамилия/отчество, email, hash пароля, статус active|deleted).
  • roles — именованные роли (например admin, editor).
  • permissions — атомарные права: resource + action + description (уникальны в паре resource/action).
  • user_roles — связь многие-ко-многим пользователей и ролей.
  • role_permissions — связь ролей и разрешений.
  • revoked_tokens — jti отозванных токенов (используется при logout).
  • audit_logs — журнал решений ACL (кто, что запросил, разрешено/запрещено).

Проверка доступа

Каждый защищенный endpoint описывает пару resource, action и использует зависимость require_permission(resource, action). Алгоритм:

  1. Токен из Authorization: Bearer ... декодируется, валидируется, проверяется в revoked_tokens.
  2. Пользователь загружается и проверяется статус active.
  3. Запрос к permissions → roles → users проверяет наличие разрешения.
  4. В audit_logs фиксируется запись и при отсутствии права возвращается 403 Forbidden. Если токен отсутствует/невалиден — 401 Unauthorized.

Основные эндпоинты

Аутентификация /auth

  • POST /auth/register — регистрация (тело: имя/фамилия/отчество?, email, пароль, повтор пароля). Возвращает пользователя.
  • POST /auth/login — email + пароль → JWT (access_token, expires_in).
  • POST /auth/logout — аннулирует текущий access token (добавляет jti в revoked_tokens).
  • GET /auth/me — профиль текущего пользователя.
  • PATCH /auth/me — обновление ФИО/пароля (с повтором для подтверждения).
  • DELETE /auth/me — мягкое удаление (status=deleted, повторный логин невозможен).

Управление доступом /admin (только permission (iam, manage))

  • GET /admin/permissions — список прав.
  • POST /admin/permissions — создание права.
  • GET /admin/roles — список ролей с правами.
  • POST /admin/roles — создание роли с перечнем permission_ids.
  • POST /admin/users/{user_id}/roles/{role_id} — назначить роль пользователю.

Мок-ресурсы /objects

  • GET /objects/ — список объектов (требует (objects, read)).
  • GET /objects/{id} — детальный просмотр (требует (objects, read)).
  • PUT /objects/{id} — обновление (требует (objects, write)).

Минимальный сценарий проверки

  1. Логин админа admin@example.com / Admin1234 → сохранить access_token.
  2. Создать нового пользователя через /auth/register.
  3. Назначить ему роль viewer через /admin/users/{user_id}/roles/{role_id}.
  4. С новым токеном пользователя вызвать GET /objects/ → 200 OK.
  5. Попробовать PUT /objects/1 → 403 Forbidden.
  6. Обновить роль на editor → PUT /objects/1 теперь 200 OK.

Почему не используем встроенный auth

  • Пароли хешируются вручную через passlib, без фреймворк-менеджеров.
  • JWT генерируются/проверяются самостоятельно через PyJWT (нет OAuth2PasswordBearer и т.п.).
  • Проверка доступа реализована отдельной зависимостью require_permission, а не стандартными permissions фреймворка.
  • Бизнес-логика вынесена в сервисы (app/services.py), доступ к БД через репозитории и Unit of Work (app/repositories.py, app/unit_of_work.py) — эндпоинты остаются тонкими.

Дальнейшее развитие (если было бы больше времени)

  • Вынести refresh токены и многофакторную аутентификацию.
  • Добавить миграции Alembic и CI для линтеров/тестов.
  • Поддержка политики на уровне записей (ABAC), кэширование прав, rate limiting.

About

minimum backend on FastAPI with its own authentication logic (JWT) and authorization through roles/permissions. No built-in auth subsystems of the framework are used.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages