template-monorepo/test/k6/lib/auth.js

110 lines
4.0 KiB
JavaScript
Raw Normal View History

2026-05-26 06:05:33 +00:00
// 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 };
}