Преминете към основното съдържание

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)stringEmail адрес на потребителя
iat (Issued At)intUnix timestamp на издаване
exp (Expires At)intUnix timestamp на изтичане
user_idstring (UUID)Уникален идентификатор на потребителя
planstringТекущ план: "free" или "pro"
device_idstringХеш на устройството (може да е празен)
providerstringТип автентикация: "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)

  1. Парсване на JWT с jwt.ParseWithClaims
  2. Проверка на signing method (само RS256)
  3. Верификация на подписа с public key
  4. Проверка на expiry с 5 секунди leeway (за clock skew)
  5. Проверка на claims type и token validity

Refresh (POST /auth/refresh)

Token refresh позволява подновяване без повторен login:

  1. Декодира подадения JWT (дори изтекъл, ако е в рамките на leeway)
  2. Извлича user_id от claims
  3. Зарежда актуалния потребител от базата данни
  4. Издава нов 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_KEYPEM съдържание на private key (директно)--
JWT_PUBLIC_KEYPEM съдържание на public key (директно)--
JWT_PRIVATE_KEY_PATHПът до private key файлkeys/private.pem
JWT_PUBLIC_KEY_PATHПът до public key файлkeys/public.pem
JWT_EXPIRY_DAYSДни до изтичане на token7

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).