2025-10-03 16:37:52 +09:00
|
|
|
/**
|
2025-10-08 16:31:42 +09:00
|
|
|
* 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.
|
2025-10-03 16:37:52 +09:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { z } from "zod";
|
|
|
|
|
|
2025-10-08 18:35:05 +09:00
|
|
|
import { emailSchema, nameSchema, passwordSchema, phoneSchema } from "../common/schema";
|
|
|
|
|
import { addressSchema, userSchema } from "../customer/schema";
|
2025-10-08 16:31:42 +09:00
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Authentication Request Schemas
|
|
|
|
|
// ============================================================================
|
2025-10-03 16:37:52 +09:00
|
|
|
|
|
|
|
|
const genderEnum = z.enum(["male", "female", "other"]);
|
|
|
|
|
|
|
|
|
|
export const loginRequestSchema = z.object({
|
|
|
|
|
email: emailSchema,
|
|
|
|
|
password: z.string().min(1, "Password is required"),
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-08 18:14:12 +09:00
|
|
|
/**
|
|
|
|
|
* 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,
|
|
|
|
|
}));
|
2025-10-03 16:37:52 +09:00
|
|
|
|
|
|
|
|
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"),
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-07 17:38:39 +09:00
|
|
|
/**
|
|
|
|
|
* 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(),
|
2025-10-03 17:33:39 +09:00
|
|
|
});
|
|
|
|
|
|
2025-10-08 10:33:33 +09:00
|
|
|
export const updateProfileRequestSchema = updateCustomerProfileRequestSchema;
|
|
|
|
|
export const updateAddressRequestSchema = updateCustomerProfileRequestSchema;
|
|
|
|
|
|
2025-10-03 16:37:52 +09:00
|
|
|
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(),
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-08 16:31:42 +09:00
|
|
|
// ============================================================================
|
|
|
|
|
// Token Schemas
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
2025-10-03 16:37:52 +09:00
|
|
|
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"),
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-08 16:31:42 +09:00
|
|
|
// ============================================================================
|
|
|
|
|
// Authentication Response Schemas (Reference User from Customer Domain)
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Auth response - returns User from customer domain
|
|
|
|
|
*/
|
2025-10-03 16:37:52 +09:00
|
|
|
export const authResponseSchema = z.object({
|
2025-10-08 16:31:42 +09:00
|
|
|
user: userSchema, // User from customer domain
|
2025-10-03 16:37:52 +09:00
|
|
|
tokens: authTokensSchema,
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-08 16:31:42 +09:00
|
|
|
/**
|
|
|
|
|
* 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(),
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-08 10:33:33 +09:00
|
|
|
// ============================================================================
|
2025-10-08 16:31:42 +09:00
|
|
|
// Inferred Types (Schema-First Approach)
|
2025-10-08 10:33:33 +09:00
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
// Request types
|
|
|
|
|
export type LoginRequest = z.infer<typeof loginRequestSchema>;
|
|
|
|
|
export type SignupRequest = z.infer<typeof signupRequestSchema>;
|
|
|
|
|
export type PasswordResetRequest = z.infer<typeof passwordResetRequestSchema>;
|
|
|
|
|
export type ResetPasswordRequest = z.infer<typeof passwordResetSchema>;
|
|
|
|
|
export type SetPasswordRequest = z.infer<typeof setPasswordRequestSchema>;
|
|
|
|
|
export type ChangePasswordRequest = z.infer<typeof changePasswordRequestSchema>;
|
|
|
|
|
export type LinkWhmcsRequest = z.infer<typeof linkWhmcsRequestSchema>;
|
|
|
|
|
export type ValidateSignupRequest = z.infer<typeof validateSignupRequestSchema>;
|
|
|
|
|
export type UpdateCustomerProfileRequest = z.infer<typeof updateCustomerProfileRequestSchema>;
|
|
|
|
|
export type AccountStatusRequest = z.infer<typeof accountStatusRequestSchema>;
|
|
|
|
|
export type SsoLinkRequest = z.infer<typeof ssoLinkRequestSchema>;
|
|
|
|
|
export type CheckPasswordNeededRequest = z.infer<typeof checkPasswordNeededRequestSchema>;
|
|
|
|
|
export type RefreshTokenRequest = z.infer<typeof refreshTokenRequestSchema>;
|
|
|
|
|
|
2025-10-08 16:31:42 +09:00
|
|
|
// Token types
|
2025-10-08 10:33:33 +09:00
|
|
|
export type AuthTokens = z.infer<typeof authTokensSchema>;
|
2025-10-08 16:31:42 +09:00
|
|
|
|
|
|
|
|
// Response types
|
2025-10-08 10:33:33 +09:00
|
|
|
export type AuthResponse = z.infer<typeof authResponseSchema>;
|
2025-10-08 16:31:42 +09:00
|
|
|
export type SignupResult = z.infer<typeof signupResultSchema>;
|
|
|
|
|
export type PasswordChangeResult = z.infer<typeof passwordChangeResultSchema>;
|
|
|
|
|
export type SsoLinkResponse = z.infer<typeof ssoLinkResponseSchema>;
|
|
|
|
|
export type CheckPasswordNeededResponse = z.infer<typeof checkPasswordNeededResponseSchema>;
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|