s-authkit
Ядро авторизации для Skillery проектов: JWT RS256, refresh-token management, Argon2 hashing.
Версия: 0.1 (первый релиз)
Лицензия: MIT
Зависимости: python-jose[cryptography], passlib[argon2], cryptography
Что входит
JWT access-токены (RS256)
JoseTokenIssuer— выпуск и верификация JWTTokenPair/TokenClaims— value objects для типизацииensure_jwt_keypair— генерация RSA 2048 ключей
Persistent refresh-tokens
IRefreshTokenStore— protocol для БД-реализацииRefreshTokenRecord— агрегат с хешированием и валидацией- Rotation с audit-trail (
rotated_from)
Password hashing
Argon2PasswordHasher— Argon2id через passlib
Примеры
1. Инициализация
from pathlib import Path
from authkit import (
JoseTokenIssuer,
Argon2PasswordHasher,
ensure_jwt_keypair,
)
# Создаём ключи (если нет)
keys_dir = Path.home() / ".myapp" / "keys"
ensure_jwt_keypair(
private_path=keys_dir / "jwt_private.pem",
public_path=keys_dir / "jwt_public.pem",
)
# Прочитаем ключи
private_key = (keys_dir / "jwt_private.pem").read_text()
public_key = (keys_dir / "jwt_public.pem").read_text()
# Создаём компоненты
hasher = Argon2PasswordHasher()
issuer = JoseTokenIssuer(
private_key_pem=private_key,
public_key_pem=public_key,
issuer="myproject.com",
access_ttl_min=15,
refresh_ttl_days=30,
refresh_store=your_store_impl, # реализуете вы
)
2. Login (выпуск пары токенов)
async def login_with_password(username: str, password: str):
# Получаем юзера из БД
user = await db.get_user_by_username(username)
if not user:
raise ValueError("User not found")
# Проверяем пароль
if not hasher.verify(password, user.password_hash):
raise ValueError("Invalid password")
# Выпускаем пару
pair = await issuer.issue_pair(
user_id=str(user.id),
company_id=str(user.active_company_id) if user.active_company_id else None,
permissions={"skill.read", "skill.install"},
)
return pair
3. Middleware (верификация токена)
from authkit import TokenError
async def auth_middleware(request, call_next):
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("Bearer "):
return Response("Unauthorized", status_code=401)
token = auth_header[7:]
try:
claims = issuer.verify_access(token)
except TokenError as e:
return Response(f"Invalid token: {e}", status_code=401)
request.state.claims = claims
return await call_next(request)
4. Refresh (rotation)
async def refresh_session(refresh_token: str):
try:
new_pair = await issuer.rotate_refresh(refresh_token)
except RefreshTokenError as e:
raise Unauthorized(f"Refresh failed: {e}")
return new_pair
5. Реализация IRefreshTokenStore
from authkit import RefreshTokenRecord, IRefreshTokenStore
class PostgresRefreshTokenStore:
def __init__(self, db_engine):
self.engine = db_engine
async def save(self, record: RefreshTokenRecord) -> None:
# INSERT/UPDATE в БД
async with self.engine.begin() as conn:
await conn.execute(
"INSERT INTO refresh_tokens (user_id, token_hash, ...) VALUES (...)"
)
async def get_by_hash(self, token_hash: str) -> RefreshTokenRecord | None:
# SELECT * FROM refresh_tokens WHERE token_hash = ?
...
async def revoke_all_for_user(self, user_id: str) -> int:
# UPDATE refresh_tokens SET revoked_at = NOW() WHERE user_id = ?
...
# Реализуете остальные методы Protocol'а
Архитектура
authkit/
├── __init__.py # Public API
├── exceptions.py # AuthKitError, TokenError, ...
├── token/
│ ├── models.py # TokenPair, TokenClaims
│ ├── jose_issuer.py # JoseTokenIssuer
│ └── keys.py # ensure_jwt_keypair
├── refresh/
│ └── models.py # RefreshTokenRecord, IRefreshTokenStore
└── password/
└── hasher.py # Argon2PasswordHasher
Тестирование
pytest tests/ # Все тесты
pytest tests/ -v --cov # С coverage report
pytest tests/ -k "test_verify" # Конкретный тест
Coverage gate: ≥ 80%.
Что НЕ входит (v0.1)
- OAuth (YandexOAuth, ExchangeCode) — в v0.2+
- PermissionResolver — остаются на стороне приложения
- Redis cache invalidation — в v0.2+ как optional
- Signing PK/SK rotation — будущая фича
- Sync обёртки — только async API в v0.1
Интеграция в существующее приложение
Типовой путь перевода существующего приложения на модуль:
- Импортирует
from authkit import ...вместо локального кода - Обёрнет
RefreshTokenRecordв ORM-адаптер для своей БД - Оставит PermissionResolver/OAuth/ExchangeCode локально
Лицензия
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
s_authkit-0.1.0.tar.gz
(26.6 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
s_authkit-0.1.0-py3-none-any.whl
(15.2 kB
view details)
File details
Details for the file s_authkit-0.1.0.tar.gz.
File metadata
- Download URL: s_authkit-0.1.0.tar.gz
- Upload date:
- Size: 26.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25423787fb0837850c124b1a5f04b50deac58ed88f7f30dfafeeca1f6d7d1f5e
|
|
| MD5 |
3fa5546af71311ba88d5fb75a1af3a2f
|
|
| BLAKE2b-256 |
9056d869ad990eacf6de9604a62c29c938aa72cc70c95ce854686ea2b90ae0e8
|
File details
Details for the file s_authkit-0.1.0-py3-none-any.whl.
File metadata
- Download URL: s_authkit-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
55a4af1376774e7edf560fe3a3313f84455bd417fa7035157777682df24e6d36
|
|
| MD5 |
706d5adddc42c99aa635017e183b0c57
|
|
| BLAKE2b-256 |
36804c4d0b91105e0c2ddc22bab24b3bcea263daa71c11476018a079212c20f9
|