81 lines
5 KiB
JavaScript
81 lines
5 KiB
JavaScript
const assert = require('assert');
|
|
const { createAuth, createLimiter, hashPassword, verifyPassword, validEmail, normEmail } = require('../brain/auth.js');
|
|
const { memoryStore } = require('../brain/db.js');
|
|
const sync = require('../brain/sync.js');
|
|
|
|
(async () => {
|
|
// Passwort-Hashing
|
|
const h = await hashPassword('geheim123');
|
|
assert.ok(h.startsWith('scrypt$'));
|
|
assert.notStrictEqual(h, await hashPassword('geheim123'), 'Salt muss variieren');
|
|
assert.ok(await verifyPassword('geheim123', h));
|
|
assert.ok(!(await verifyPassword('falsch123', h)));
|
|
assert.ok(validEmail(normEmail(' Test@Example.COM ')));
|
|
assert.ok(!validEmail('kein-at.de') && !validEmail('a@b') && !validEmail('a b@c.de'));
|
|
|
|
let t = 1_000_000;
|
|
const kv = memoryStore();
|
|
const auth = createAuth(kv, { now: () => t, loginLimiter: createLimiter(1000, 1000, () => t), registerLimiter: createLimiter(1000, 1000, () => t) });
|
|
|
|
assert.strictEqual((await auth.register({ email: 'x', password: 'abcdefgh' }, 'ip')).body.error, 'invalid_email');
|
|
assert.strictEqual((await auth.register({ email: 'a@b.de', password: 'kurz' }, 'ip')).body.error, 'password_too_short');
|
|
const r = await auth.register({ email: 'Byte@Test.de', password: 'pixelpixel' }, 'ip');
|
|
assert.strictEqual(r.status, 201); assert.strictEqual(r.body.user.email, 'byte@test.de'); assert.ok(r.body.token.length >= 40);
|
|
assert.strictEqual((await auth.register({ email: 'byte@test.de', password: 'pixelpixel' }, 'ip')).status, 409);
|
|
// Passwort-Hash und Token nie im Klartext gespeichert
|
|
const raw = JSON.stringify(await kv.get('sessions', require('../brain/auth.js').tokenHash(r.body.token)));
|
|
assert.ok(raw && !raw.includes(r.body.token));
|
|
|
|
// Login generisch
|
|
const bad1 = await auth.login({ email: 'byte@test.de', password: 'falschfalsch' }, 'ip');
|
|
const bad2 = await auth.login({ email: 'niemand@test.de', password: 'falschfalsch' }, 'ip');
|
|
assert.deepStrictEqual([bad1.status, bad1.body], [bad2.status, bad2.body]);
|
|
const l = await auth.login({ email: 'BYTE@test.de', password: 'pixelpixel' }, 'ip');
|
|
assert.strictEqual(l.status, 200);
|
|
const B = 'Bearer ' + l.body.token;
|
|
assert.strictEqual((await auth.me(B)).body.user.email, 'byte@test.de');
|
|
assert.strictEqual((await auth.me('Bearer ungueltigungueltigungueltig')).status, 401);
|
|
assert.strictEqual((await auth.me(undefined)).status, 401);
|
|
|
|
// Sync an Konto gebunden
|
|
const a = await auth.authenticate(B);
|
|
assert.strictEqual((await sync.handleAsync(kv, 'PUT', 'user:' + a.user.id, { version: 1, state: { xp: 42 } })).status, 200);
|
|
assert.strictEqual((await sync.handleAsync(kv, 'GET', 'user:' + a.user.id)).body.state.xp, 42);
|
|
assert.strictEqual((await sync.handleAsync(kv, 'GET', null)).status, 401);
|
|
|
|
// Passwort ändern beendet andere Sessions
|
|
const B1 = 'Bearer ' + r.body.token;
|
|
assert.strictEqual((await auth.changePassword(B, { currentPassword: 'falsch', newPassword: 'neuespasswort' }, 'ip')).status, 401);
|
|
assert.strictEqual((await auth.changePassword(B, { currentPassword: 'pixelpixel', newPassword: 'kurz' }, 'ip')).status, 400);
|
|
assert.strictEqual((await auth.changePassword(B, { currentPassword: 'pixelpixel', newPassword: 'neuespasswort' }, 'ip')).status, 200);
|
|
assert.strictEqual((await auth.me(B1)).status, 401);
|
|
assert.strictEqual((await auth.me(B)).status, 200);
|
|
assert.strictEqual((await auth.login({ email: 'byte@test.de', password: 'pixelpixel' }, 'ip')).status, 401);
|
|
|
|
// Logout
|
|
const l2 = await auth.login({ email: 'byte@test.de', password: 'neuespasswort' }, 'ip');
|
|
assert.strictEqual((await auth.logout('Bearer ' + l2.body.token)).status, 200);
|
|
assert.strictEqual((await auth.me('Bearer ' + l2.body.token)).status, 401);
|
|
|
|
// Ablauf nach 30 Tagen
|
|
t += 31 * 24 * 3600 * 1000;
|
|
assert.strictEqual((await auth.me(B)).status, 401);
|
|
|
|
// Konto löschen
|
|
const l3 = await auth.login({ email: 'byte@test.de', password: 'neuespasswort' }, 'ip');
|
|
const B3 = 'Bearer ' + l3.body.token;
|
|
assert.strictEqual((await auth.deleteAccount(B3, { password: 'falsch' }, 'ip')).status, 401);
|
|
assert.strictEqual((await auth.deleteAccount(B3, { password: 'neuespasswort' }, 'ip')).status, 200);
|
|
assert.strictEqual((await auth.me(B3)).status, 401);
|
|
assert.strictEqual(await kv.get('sync', 'user:' + a.user.id), null);
|
|
assert.strictEqual((await auth.login({ email: 'byte@test.de', password: 'neuespasswort' }, 'ip')).status, 401);
|
|
|
|
// Rate-Limit
|
|
const auth2 = createAuth(memoryStore(), { loginLimiter: createLimiter(3, 60000), registerLimiter: createLimiter(2, 60000) });
|
|
for (let i = 0; i < 3; i++) await auth2.login({ email: 'q@q.de', password: 'xxxxxxxx' }, 'ip9');
|
|
assert.strictEqual((await auth2.login({ email: 'q@q.de', password: 'xxxxxxxx' }, 'ip9')).status, 429);
|
|
await auth2.register({ email: 'r1@q.de', password: 'xxxxxxxx' }, 'ip8'); await auth2.register({ email: 'r2@q.de', password: 'xxxxxxxx' }, 'ip8');
|
|
assert.strictEqual((await auth2.register({ email: 'r3@q.de', password: 'xxxxxxxx' }, 'ip8')).status, 429);
|
|
|
|
console.log('auth.test.js OK');
|
|
})().catch((e) => { console.error(e); process.exit(1); });
|