82 lines
2.3 KiB
JavaScript
82 lines
2.3 KiB
JavaScript
|
|
// smoke: POST /api/v1/auth/login/mfa
|
|||
|
|
//
|
|||
|
|
// Covers:
|
|||
|
|
// login → mfa_required(已啟用 TOTP 時不回 token)
|
|||
|
|
// 403 — TOTP 錯誤(29505000)
|
|||
|
|
// 404 — challenge 不存在(28301000)
|
|||
|
|
// 403 — tenant slug 不符(28505000)
|
|||
|
|
// 400 — 缺少 code / challenge_id
|
|||
|
|
//
|
|||
|
|
// Happy path(login → login/mfa → JWT)見 journeys/login_mfa_full.js
|
|||
|
|
import { post, checkError } from '../lib/http.js';
|
|||
|
|
import { cfg } from '../lib/config.js';
|
|||
|
|
import { registerAndConfirm, loginExpectMFA } from '../lib/auth.js';
|
|||
|
|
import { enrollTOTP } from '../lib/member.js';
|
|||
|
|
|
|||
|
|
export const options = {
|
|||
|
|
vus: 1,
|
|||
|
|
iterations: 1,
|
|||
|
|
thresholds: { checks: ['rate==1.0'] },
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default function () {
|
|||
|
|
const { identity, tokens } = registerAndConfirm();
|
|||
|
|
const bearer = { Authorization: `Bearer ${tokens.access_token}` };
|
|||
|
|
const { otpauthUrl } = enrollTOTP(bearer);
|
|||
|
|
|
|||
|
|
const mfa = loginExpectMFA({
|
|||
|
|
email: identity.email,
|
|||
|
|
password: identity.password,
|
|||
|
|
});
|
|||
|
|
if (mfa.access_token) {
|
|||
|
|
throw new Error('login with TOTP enrolled should not return access_token');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// bad TOTP
|
|||
|
|
checkError(
|
|||
|
|
post('/api/v1/auth/login/mfa', {
|
|||
|
|
tenant_slug: cfg.tenantSlug,
|
|||
|
|
challenge_id: mfa.mfa_challenge_id,
|
|||
|
|
code: '000000',
|
|||
|
|
}),
|
|||
|
|
'POST /auth/login/mfa (bad totp)',
|
|||
|
|
403,
|
|||
|
|
29505000,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// unknown challenge — Redis miss 目前回 500(28201000)
|
|||
|
|
checkError(
|
|||
|
|
post('/api/v1/auth/login/mfa', {
|
|||
|
|
tenant_slug: cfg.tenantSlug,
|
|||
|
|
challenge_id: '00000000-0000-0000-0000-000000000000',
|
|||
|
|
code: '123456',
|
|||
|
|
}),
|
|||
|
|
'POST /auth/login/mfa (unknown challenge)',
|
|||
|
|
500,
|
|||
|
|
28201000,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// unknown tenant slug(resolveTenant 失敗)
|
|||
|
|
checkError(
|
|||
|
|
post('/api/v1/auth/login/mfa', {
|
|||
|
|
tenant_slug: 'wrong-tenant-slug',
|
|||
|
|
challenge_id: mfa.mfa_challenge_id,
|
|||
|
|
code: '123456',
|
|||
|
|
}),
|
|||
|
|
'POST /auth/login/mfa (unknown tenant)',
|
|||
|
|
404,
|
|||
|
|
29301000,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// missing fields
|
|||
|
|
const missing = post('/api/v1/auth/login/mfa', { tenant_slug: cfg.tenantSlug });
|
|||
|
|
if (missing.status !== 400) {
|
|||
|
|
throw new Error(`login/mfa missing fields: expected 400 got ${missing.status}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// otpauthUrl kept for journey reuse sanity (not used further in smoke)
|
|||
|
|
if (!otpauthUrl) {
|
|||
|
|
throw new Error('enrollTOTP did not return otpauthUrl');
|
|||
|
|
}
|
|||
|
|
}
|