// Auth flow helpers — register / confirm / login / refresh / logout. // All requests go through lib/http.js; OTP comes from lib/otp.js. import { post } from './http.js'; import { checkEnvelope } from './http.js'; import { cfg, unique } from './config.js'; import { fetchEmailOTP } from './otp.js'; // makeIdentity returns a unique (email, password, display_name) tuple for the // current VU iteration. Use to avoid collisions in concurrent runs. export function makeIdentity(prefix = 'k6') { const slug = unique(prefix); return { email: `${slug}@k6.local`, password: 'K6-StrongPass-1!', displayName: `K6 ${slug}`, }; } // registerEmail calls POST /api/v1/auth/register and returns the parsed // RegisterData (challenge_id, expires_in, uid). export function registerEmail({ tenantSlug = cfg.tenantSlug, inviteCode = cfg.inviteCode, email, password, displayName, language = 'zh-TW', termsVersion = '2025-01-01', marketingOptIn = false } = {}) { const res = post('/api/v1/auth/register', { tenant_slug: tenantSlug, invite_code: inviteCode, email, password, display_name: displayName, language, accept_terms_version: termsVersion, marketing_opt_in: marketingOptIn, }); const body = checkEnvelope(res, 'POST /auth/register'); if (!body.data || !body.data.challenge_id) { throw new Error(`register: missing challenge_id in ${res.body}`); } return body.data; } // confirmRegister fetches the OTP from MailHog then calls // POST /api/v1/auth/register/confirm. Returns AuthTokenData. export function confirmRegister({ tenantSlug = cfg.tenantSlug, email, challengeId }) { const code = fetchEmailOTP(email); const res = post('/api/v1/auth/register/confirm', { tenant_slug: tenantSlug, challenge_id: challengeId, code, }); const body = checkEnvelope(res, 'POST /auth/register/confirm'); if (!body.data || !body.data.access_token) { throw new Error(`register/confirm: missing access_token in ${res.body}`); } return body.data; } // resendRegister calls POST /api/v1/auth/register/resend. // Returns RegisterData (challenge_id, expires_in, uid). export function resendRegister({ tenantSlug = cfg.tenantSlug, challengeId }) { const res = post('/api/v1/auth/register/resend', { tenant_slug: tenantSlug, challenge_id: challengeId, }); return checkEnvelope(res, 'POST /auth/register/resend').data; } // login calls POST /api/v1/auth/login. Returns AuthTokenData. export function login({ tenantSlug = cfg.tenantSlug, email, password }) { const res = post('/api/v1/auth/login', { tenant_slug: tenantSlug, email, password, }); const body = checkEnvelope(res, 'POST /auth/login'); if (!body.data || !body.data.access_token) { throw new Error(`login: missing access_token in ${res.body}`); } return body.data; } // refreshToken calls POST /api/v1/auth/token/refresh. export function refreshToken({ refreshToken }) { const res = post('/api/v1/auth/token/refresh', { refresh_token: refreshToken }); const body = checkEnvelope(res, 'POST /auth/token/refresh'); if (!body.data || !body.data.access_token) { throw new Error(`token/refresh: missing access_token in ${res.body}`); } return body.data; } // logout calls POST /api/v1/auth/logout (requires Bearer access_token). export function logout({ accessToken }) { const res = post('/api/v1/auth/logout', null, { Authorization: `Bearer ${accessToken}` }); const body = checkEnvelope(res, 'POST /auth/logout'); return body.data; } // registerAndConfirm is the most common building block: makes an identity, // runs register → confirm, returns { identity, tokens, registerData }. export function registerAndConfirm({ tenantSlug = cfg.tenantSlug, inviteCode = cfg.inviteCode } = {}) { const identity = makeIdentity(); const reg = registerEmail({ tenantSlug, inviteCode, email: identity.email, password: identity.password, displayName: identity.displayName, }); const tokens = confirmRegister({ tenantSlug, email: identity.email, challengeId: reg.challenge_id }); return { identity, tokens, registerData: reg }; }