Commit 73855b03 authored by ThinhNC's avatar ThinhNC

fix: auth service logic and validation schemas for user management

parent 5d8c61c1
...@@ -480,56 +480,60 @@ export class AuthService { ...@@ -480,56 +480,60 @@ export class AuthService {
console.warn('[ZaloAuth] fetchZaloProfile caught error:', err); console.warn('[ZaloAuth] fetchZaloProfile caught error:', err);
} }
// 2. Lấy và chuẩn hóa số điện thoại (từ phoneToken hoặc phoneNumber) // 2. Lấy và chuẩn hóa số điện thoại (từ phoneToken hoặc phoneNumber nếu có thể giải mã)
let resolvedPhone = dto.phoneNumber; let resolvedPhone = dto.phoneNumber;
if (dto.phoneToken) { if (dto.phoneToken) {
const phoneResponse = await this.fetchZaloPhoneNumber(accessToken, dto.phoneToken, appSecret); try {
if ( const phoneResponse = await this.fetchZaloPhoneNumber(accessToken, dto.phoneToken, appSecret);
!phoneResponse || if (phoneResponse && phoneResponse.data?.number) {
(phoneResponse.error !== undefined && phoneResponse.error !== 0) || resolvedPhone = phoneResponse.data.number;
!phoneResponse.data?.number } else if (phoneResponse?.error === -501) {
) { console.warn('[ZaloAuth] Zalo Phone API limited by IP location (-501). Authenticating via Zalo ID.');
console.error('[ZaloAuth] fetchZaloPhoneNumber failed:', phoneResponse); } else if (phoneResponse && phoneResponse.error !== undefined && phoneResponse.error !== 0) {
throw new AppError( console.warn('[ZaloAuth] fetchZaloPhoneNumber returned error:', phoneResponse);
phoneResponse?.message ? `Zalo Phone Error: ${phoneResponse.message}` : 'Failed to decode phone number from Zalo token', }
401, } catch (err) {
ERROR_CODE.INVALID_CREDENTIALS, console.warn('[ZaloAuth] fetchZaloPhoneNumber caught error:', err);
);
} }
resolvedPhone = phoneResponse.data.number;
} }
if (!resolvedPhone) { if (resolvedPhone) {
throw new AppError('Phone number is required', 400, ERROR_CODE.VALIDATION_ERROR); // Chuẩn hóa số điện thoại: +84... hoặc 84... -> 0...
resolvedPhone = resolvedPhone.replace(/^\+84/, '0').replace(/^84/, '0');
} }
// Chuẩn hóa số điện thoại: +84... hoặc 84... -> 0... // Đảm bảo có Zalo ID làm mã định danh
resolvedPhone = resolvedPhone.replace(/^\+84/, '0').replace(/^84/, '0');
if (!zaloId) { if (!zaloId) {
zaloId = `zalo_${resolvedPhone}`; if (resolvedPhone) {
zaloId = `zalo_${resolvedPhone}`;
} else {
throw new AppError('Unable to identify Zalo user. Please grant basic permissions.', 400, ERROR_CODE.VALIDATION_ERROR);
}
} }
// 3. Tìm hoặc tạo user theo SĐT // 3. Tìm hoặc tạo user
let user = await this.repository.findByPhone(resolvedPhone); // A. Tìm theo liên kết mạng xã hội Zalo ID trước
let user: any = await this.repository.findBySocial('zalo', zaloId);
if (user) { // B. Nếu chưa tìm thấy theo Zalo ID và có SĐT, tìm theo SĐT
// User đã tồn tại — liên kết Zalo ID nếu chưa có if (!user && resolvedPhone) {
const existing = await this.repository.findBySocial('zalo', zaloId); user = await this.repository.findByPhone(resolvedPhone);
if (!existing) { if (user) {
await this.repository.linkSocialAccount(user.id, 'zalo', zaloId); await this.repository.linkSocialAccount(user.id, 'zalo', zaloId);
} }
} else { }
// User chưa tồn tại — tạo mới từ Zalo profile
// C. Nếu user chưa tồn tại, tạo mới
if (!user) {
const role = await this.repository.findRoleByName(SYSTEM_ROLES.USER); const role = await this.repository.findRoleByName(SYSTEM_ROLES.USER);
if (!role) { if (!role) {
throw new AppError('Default role not found', 500, ERROR_CODE.INTERNAL_SERVER_ERROR); throw new AppError('Default role not found', 500, ERROR_CODE.INTERNAL_SERVER_ERROR);
} }
user = await this.repository.createSocialUser({ user = await this.repository.createSocialUser({
fullName: zaloName || undefined, fullName: zaloName || 'Người dùng Zalo',
avatarUrl: zaloAvatarUrl || undefined, avatarUrl: zaloAvatarUrl || undefined,
phoneNumber: resolvedPhone, phoneNumber: resolvedPhone || undefined,
roleId: role.id, roleId: role.id,
provider: 'zalo', provider: 'zalo',
providerUserId: zaloId, providerUserId: zaloId,
......
...@@ -22,9 +22,9 @@ export const zaloLoginSchema = z ...@@ -22,9 +22,9 @@ export const zaloLoginSchema = z
name: z.string().optional(), name: z.string().optional(),
avatar: z.string().optional(), avatar: z.string().optional(),
}) })
.refine((data) => data.phoneToken || data.phoneNumber, { .refine((data) => data.phoneToken || data.phoneNumber || data.zaloId, {
message: 'Either phoneToken or phoneNumber must be provided', message: 'Either phoneToken, phoneNumber, or zaloId must be provided',
path: ['phoneToken'], path: ['accessToken'],
}); });
export const refreshSchema = z.object({ export const refreshSchema = z.object({
...@@ -135,4 +135,3 @@ export const sessionQuerySchema = z.object({ ...@@ -135,4 +135,3 @@ export const sessionQuerySchema = z.object({
export const revokeOtherSessionsSchema = z.object({ export const revokeOtherSessionsSchema = z.object({
refreshToken: z.string().optional(), refreshToken: z.string().optional(),
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment