Токени і доступ
У сервері співіснують чотири незалежні механізми доступу. Плутанина між ними — найчастіша причина 401 під час написання клієнта.
Огляд
| Токен | Хто отримує | Час життя | Навіщо |
|---|---|---|---|
artToken | будь-який слухач | 1 година | арти, аватари, тексти, ключ XOR |
audioToken | слухач з artToken | коротко | доступ до аудіо |
| JWT адміна | після входу | сесія | усі /api/admin/* |
| гостьовий | запрошений гість | сесія | вхід у гостьову кімнату |
artToken
Видається через сокет, не через HTTP: клієнт надсилає listener_init і отримує listener_uid з токеном усередині.
Токен прив'язаний до uid, який сервер виводить з IP-адреси з'єднання. Клієнт не може обрати собі ідентичність, і слухачі за спільним NAT отримають один uid на всіх.
Передається заголовком X-Art-Token або параметром ?token=.
audioToken
Обмінюється на artToken:
GET /api/audio-key
X-Art-Token: <artToken>
X-Listener-Uid: <uid>Відповідь містить expiresIn у секундах. Оновлюйте завчасно — референсний клієнт робить це за 30 секунд до кінця.
Ендпоінти аудіо вимагають обидва токени одночасно.
XOR-обгортка
Арти, аватари й тексти пісень віддаються не в чистому вигляді, а поксорені з ключем, що виводиться з artToken.
Ключ — це середній сегмент токена, закодований base64url:
export const xorDecrypt = (buf, token) => {
const b64 = (token.split('.')[1] ?? '').replace(/-/g, '+').replace(/_/g, '/');
const keyRaw = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
const enc = new Uint8Array(buf);
const dec = new Uint8Array(enc.length);
for (let i = 0; i < enc.length; i++) dec[i] = enc[i] ^ keyRaw[i % keyRaw.length];
return dec;
};Аудіо цим не шифрується
Попри назву audioToken, самі аудіофайли не проходять через XOR. Токен там — лише перепустка, а байти віддаються як є. XOR стосується артів, аватарів і текстів, і ключем служить artToken.
Це обфускація, а не шифрування
Ключ видається клієнту на запит, а алгоритм відкритий. Такий захист відсіює випадкове вигрібання ресурсів чужими сторінками, але не є засобом безпеки й не має вважатися таким. Не покладайтеся на нього для чогось справді закритого.
JWT адміністратора
POST /api/admin/login
{ "login": "…", "password": "…" }Далі Authorization: Bearer <token> до всіх /api/admin/*. Частина ендпоінтів додатково приймає токен у query — це зроблено для тегів <audio src>, де заголовок задати неможливо.
Понад JWT більшість адмінських ендпоінтів перевіряє ще й привілей — див. Привілеї.