/** * Auth Domain - Types * * Contains ONLY authentication mechanism types: * - Login, Signup, Password Management * - Token Management * - MFA, SSO * * User entity types are in customer domain. * Auth responses reference User from customer domain. */ import { z } from "zod"; import { emailSchema, nameSchema, passwordSchema, phoneSchema } from "../common/schema"; import { addressSchema, userSchema } from "../customer/schema"; // ============================================================================ // Authentication Request Schemas // ============================================================================ const genderEnum = z.enum(["male", "female", "other"]); export const loginRequestSchema = z.object({ email: emailSchema, password: z.string().min(1, "Password is required"), }); /** * Base signup input schema (before API transformation) * Used by frontend forms - can be extended with UI-specific fields (e.g., confirmPassword) */ export const signupInputSchema = z.object({ email: emailSchema, password: passwordSchema, firstName: nameSchema, lastName: nameSchema, company: z.string().optional(), phone: phoneSchema, sfNumber: z.string().min(6, "Customer number must be at least 6 characters"), address: addressSchema.optional(), nationality: z.string().optional(), dateOfBirth: z.string().optional(), gender: genderEnum.optional(), acceptTerms: z.boolean(), marketingConsent: z.boolean().optional(), }); /** * Signup request schema with API transformation * Transforms camelCase fields to match WHMCS API expectations */ export const signupRequestSchema = signupInputSchema.transform(data => ({ ...data, firstname: data.firstName, lastname: data.lastName, companyname: data.company, phonenumber: data.phone, })); export const passwordResetRequestSchema = z.object({ email: emailSchema }); export const passwordResetSchema = z.object({ token: z.string().min(1, "Reset token is required"), password: passwordSchema, }); export const setPasswordRequestSchema = z.object({ email: emailSchema, password: passwordSchema, }); export const changePasswordRequestSchema = z.object({ currentPassword: z.string().min(1, "Current password is required"), newPassword: passwordSchema, }); export const linkWhmcsRequestSchema = z.object({ email: emailSchema, password: z.string().min(1, "Password is required"), }); export const validateSignupRequestSchema = z.object({ sfNumber: z.string().min(1, "Customer number is required"), }); /** * Schema for updating customer profile in WHMCS (single source of truth) * All fields optional - only send what needs to be updated * Can update profile fields and/or address fields in a single request */ export const updateCustomerProfileRequestSchema = z.object({ // Basic profile firstname: nameSchema.optional(), lastname: nameSchema.optional(), companyname: z.string().max(100).optional(), phonenumber: phoneSchema.optional(), // Address (optional fields for partial updates) address1: z.string().max(200).optional(), address2: z.string().max(200).optional(), city: z.string().max(100).optional(), state: z.string().max(100).optional(), postcode: z.string().max(20).optional(), country: z.string().length(2).optional(), // ISO country code // Additional language: z.string().max(10).optional(), }); export const updateProfileRequestSchema = updateCustomerProfileRequestSchema; export const updateAddressRequestSchema = updateCustomerProfileRequestSchema; export const accountStatusRequestSchema = z.object({ email: emailSchema, }); export const ssoLinkRequestSchema = z.object({ destination: z.string().optional(), }); export const checkPasswordNeededRequestSchema = z.object({ email: emailSchema, }); export const refreshTokenRequestSchema = z.object({ refreshToken: z.string().min(1, "Refresh token is required").optional(), deviceId: z.string().optional(), }); // ============================================================================ // Token Schemas // ============================================================================ export const authTokensSchema = z.object({ accessToken: z.string().min(1, "Access token is required"), refreshToken: z.string().min(1, "Refresh token is required"), expiresAt: z.string().min(1, "Access token expiry required"), refreshExpiresAt: z.string().min(1, "Refresh token expiry required"), tokenType: z.literal("Bearer"), }); // ============================================================================ // Authentication Response Schemas (Reference User from Customer Domain) // ============================================================================ /** * Auth response - returns User from customer domain */ export const authResponseSchema = z.object({ user: userSchema, // User from customer domain tokens: authTokensSchema, }); /** * Signup result - returns User from customer domain */ export const signupResultSchema = z.object({ user: userSchema, // User from customer domain tokens: authTokensSchema, }); /** * Password change result - returns User from customer domain */ export const passwordChangeResultSchema = z.object({ user: userSchema, // User from customer domain tokens: authTokensSchema, }); /** * SSO link response */ export const ssoLinkResponseSchema = z.object({ url: z.url(), expiresAt: z.string(), }); /** * Check password needed response */ export const checkPasswordNeededResponseSchema = z.object({ needsPasswordSet: z.boolean(), userExists: z.boolean(), email: z.email().optional(), }); // ============================================================================ // Inferred Types (Schema-First Approach) // ============================================================================ // Request types export type LoginRequest = z.infer; export type SignupRequest = z.infer; export type PasswordResetRequest = z.infer; export type ResetPasswordRequest = z.infer; export type SetPasswordRequest = z.infer; export type ChangePasswordRequest = z.infer; export type LinkWhmcsRequest = z.infer; export type ValidateSignupRequest = z.infer; export type UpdateCustomerProfileRequest = z.infer; export type AccountStatusRequest = z.infer; export type SsoLinkRequest = z.infer; export type CheckPasswordNeededRequest = z.infer; export type RefreshTokenRequest = z.infer; // Token types export type AuthTokens = z.infer; // Response types export type AuthResponse = z.infer; export type SignupResult = z.infer; export type PasswordChangeResult = z.infer; export type SsoLinkResponse = z.infer; export type CheckPasswordNeededResponse = z.infer; // ============================================================================ // Error Types // ============================================================================ export interface AuthError { code: "INVALID_CREDENTIALS" | "EMAIL_NOT_VERIFIED" | "ACCOUNT_LOCKED" | "MFA_REQUIRED" | "INVALID_TOKEN" | "TOKEN_EXPIRED" | "PASSWORD_TOO_WEAK" | "EMAIL_ALREADY_EXISTS" | "WHMCS_ACCOUNT_NOT_FOUND" | "SALESFORCE_ACCOUNT_NOT_FOUND" | "LINKING_FAILED"; message: string; details?: unknown; }