JWT Token Management
Licensing Server използва RS256 (RSA-SHA256) подписани JWT токени за автентикация на API заявки. Token-ите се издават при login (OAuth или email) и се валидират от middleware при всяка заявка, изискваща автентикация.
Token структура
Claims
{
"sub": "user@example.com",
"iat": 1710000000,
"exp": 1710604800,
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"plan": "free",
"device_id": "DEVICE_HASH",
"provider": "google"
}
| Claim | Тип | Описание |
|---|---|---|
sub (Subject) | string | Email адрес на потребителя |
iat (Issued At) | int | Unix timestamp на издаване |
exp (Expires At) | int | Unix timestamp на изтичане |
user_id | string (UUID) | Уникален идентификатор на потребителя |
plan | string | Текущ план: "free" или "pro" |
device_id | string | Хеш на устройството (може да е празен) |
provider | string | Тип автентикация: "google", "azure", "github", "email" |
Go struct
type JWTClaims struct {
jwt.RegisteredClaims
UserID string `json:"user_id"`
Plan string `json:"plan"`
DeviceID string `json:"device_id"`
Provider string `json:"provider"`
}
Token Lifecycle
Създаване (CreateAccessToken)
Токенът се създава с:
- Algorithm:
RS256(RSA PKCS1 v1.5 с SHA-256) - Expiry:
JWT_EXPIRY_DAYSдни (default: 7) - Subject: email на потребителя
Валидация (DecodeToken)
- Парсване на JWT с
jwt.ParseWithClaims - Проверка на signing method (само
RS256) - Верификация на подписа с public key
- Проверка на expiry с 5 секунди leeway (за clock skew)
- Проверка на claims type и token validity
Refresh (POST /auth/refresh)
Token refresh позволява подновяване без повторен login:
- Декодира подадения JWT (дори изтекъл, ако е в рамките на leeway)
- Извлича
user_idот claims - Зарежда актуалния потребител от базата данни
- Издава нов JWT с актуални план и email (от DB, не от стария token)
Важно: Refresh-ът не удължава стария token, а издава изцяло нов. Това гарантира, че промени в плана (напр. upgrade на Pro) се отразяват веднага.
RSA Key Pair
Генериране
# Генериране на private key
openssl genrsa -out keys/private.pem 2048
# Извличане на public key
openssl rsa -in keys/private.pem -pubout -out keys/public.pem
Зареждане
Ключовете се зареждат по следния приоритет:
| Env var | Описание | Default |
|---|---|---|
JWT_PRIVATE_KEY | PEM съдържание на private key (директно) | -- |
JWT_PUBLIC_KEY | PEM съдържание на public key (директно) | -- |
JWT_PRIVATE_KEY_PATH | Път до private key файл | keys/private.pem |
JWT_PUBLIC_KEY_PATH | Път до public key файл | keys/public.pem |
JWT_EXPIRY_DAYS | Дни до изтичане на token | 7 |
Production: В Azure Container Apps ключовете се подават като secrets чрез JWT_PRIVATE_KEY / JWT_PUBLIC_KEY environment variables (PEM съдържание).
Development: Ключовете се четат от файлове в licensing-go/keys/ (gitignored).
Поддържани формати
- PKCS#1 (
BEGIN RSA PRIVATE KEY) - PKCS#8 (
BEGIN PRIVATE KEY) - PKIX за public key (
BEGIN PUBLIC KEY)
Auth Middleware
RequireAuth
Задължителна автентикация -- отхвърля заявки без валиден JWT.
Използва се за: /license/activate, /account/status, /billing/create-checkout, /account/portal-url
OptionalAuth
Опционална автентикация -- ако JWT е подаден и валиден, зарежда user-а; в противен случай задава nil и продължава.
Използва се за: /usage/record, /usage/quota-status
Token Extraction
Token-ът се извлича от Authorization header-а:
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Специален случай: /billing/choose-plan приема JWT като query parameter ?token=..., тъй като страницата се отваря директно в browser (без възможност за Bearer header).