Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
97 lines
3.0 KiB
TypeScript
97 lines
3.0 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { confirmTotpEnrollment } from '../src/webui/totp';
|
|
import {
|
|
completeWebuiLogin,
|
|
invalidateOtherSessions,
|
|
type WebuiLoginAuth,
|
|
} from '../src/webui/webui-login';
|
|
|
|
const RFC_SECRET = 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ';
|
|
const T59_MS = 59_000;
|
|
const T59_CODE = '287082';
|
|
const PASSWORD = 'Correct-Horse-1!';
|
|
|
|
function authWithoutTotp(): WebuiLoginAuth {
|
|
return {
|
|
verify: (password) => password === PASSWORD,
|
|
totpEnabled: () => false,
|
|
totpState: () => undefined,
|
|
mustChangePassword: () => false,
|
|
};
|
|
}
|
|
|
|
describe('completeWebuiLogin', () => {
|
|
it('rejects a wrong password without mentioning TOTP', () => {
|
|
expect(completeWebuiLogin(authWithoutTotp(), { password: 'nope' }, T59_MS)).toEqual({
|
|
kind: 'bad-password',
|
|
});
|
|
});
|
|
|
|
it('logs in with only a password when 2FA is off', () => {
|
|
expect(completeWebuiLogin(authWithoutTotp(), { password: PASSWORD }, T59_MS)).toEqual({
|
|
kind: 'ok',
|
|
mustChangePassword: false,
|
|
});
|
|
});
|
|
|
|
it('asks for TOTP after a correct password when 2FA is on', () => {
|
|
const enabled = confirmTotpEnrollment({
|
|
password: PASSWORD,
|
|
secret: RFC_SECRET,
|
|
code: T59_CODE,
|
|
label: 'SnowLuma (testhost)',
|
|
atMs: T59_MS,
|
|
});
|
|
const auth: WebuiLoginAuth = {
|
|
verify: (password) => password === PASSWORD,
|
|
totpEnabled: () => true,
|
|
totpState: () => enabled.state,
|
|
mustChangePassword: () => false,
|
|
};
|
|
expect(completeWebuiLogin(auth, { password: PASSWORD }, T59_MS)).toEqual({
|
|
kind: 'needs-totp',
|
|
});
|
|
});
|
|
|
|
it('accepts password plus TOTP and returns the updated totp state', () => {
|
|
const enabled = confirmTotpEnrollment({
|
|
password: PASSWORD,
|
|
secret: RFC_SECRET,
|
|
code: T59_CODE,
|
|
label: 'SnowLuma (testhost)',
|
|
atMs: T59_MS,
|
|
});
|
|
const auth: WebuiLoginAuth = {
|
|
verify: (password) => password === PASSWORD,
|
|
totpEnabled: () => true,
|
|
totpState: () => enabled.state,
|
|
mustChangePassword: () => true,
|
|
};
|
|
const result = completeWebuiLogin(
|
|
auth,
|
|
{ password: PASSWORD, totp: '081804' },
|
|
1_111_111_109_000,
|
|
);
|
|
expect(result.kind).toBe('ok');
|
|
|
|
if (result.kind !== 'ok') {
|
|
return;
|
|
}
|
|
|
|
expect(result.mustChangePassword).toBe(true);
|
|
expect(result.totpState?.lastUsedStep).toBeGreaterThan(enabled.state.lastUsedStep);
|
|
});
|
|
});
|
|
|
|
describe('invalidateOtherSessions', () => {
|
|
it('keeps the current session and drops the rest', () => {
|
|
const sessions = new Map([
|
|
['keep', { n: 1 }],
|
|
['drop-a', { n: 2 }],
|
|
['drop-b', { n: 3 }],
|
|
]);
|
|
invalidateOtherSessions(sessions, 'keep');
|
|
expect([...sessions.keys()]).toEqual(['keep']);
|
|
});
|
|
});
|