@warriorteam/redai-zalo-sdk 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +563 -550
  2. package/dist/index.d.ts +0 -2
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -5
  5. package/dist/index.js.map +1 -1
  6. package/dist/services/article.service.d.ts +1 -1
  7. package/dist/services/article.service.d.ts.map +1 -1
  8. package/dist/services/article.service.js +24 -16
  9. package/dist/services/article.service.js.map +1 -1
  10. package/dist/services/auth.service.d.ts +1 -0
  11. package/dist/services/auth.service.d.ts.map +1 -1
  12. package/dist/services/auth.service.js +23 -9
  13. package/dist/services/auth.service.js.map +1 -1
  14. package/dist/services/consultation.service.d.ts +63 -16
  15. package/dist/services/consultation.service.d.ts.map +1 -1
  16. package/dist/services/consultation.service.js +264 -49
  17. package/dist/services/consultation.service.js.map +1 -1
  18. package/dist/services/general-message.service.d.ts +2 -25
  19. package/dist/services/general-message.service.d.ts.map +1 -1
  20. package/dist/services/general-message.service.js +11 -112
  21. package/dist/services/general-message.service.js.map +1 -1
  22. package/dist/services/group-management.service.d.ts +1 -1
  23. package/dist/services/group-management.service.d.ts.map +1 -1
  24. package/dist/services/group-management.service.js +59 -27
  25. package/dist/services/group-management.service.js.map +1 -1
  26. package/dist/services/group-message.service.d.ts +1 -1
  27. package/dist/services/group-message.service.d.ts.map +1 -1
  28. package/dist/services/group-message.service.js +49 -23
  29. package/dist/services/group-message.service.js.map +1 -1
  30. package/dist/services/message-management.service.d.ts +21 -2
  31. package/dist/services/message-management.service.d.ts.map +1 -1
  32. package/dist/services/message-management.service.js +83 -7
  33. package/dist/services/message-management.service.js.map +1 -1
  34. package/dist/services/oa.service.d.ts +1 -0
  35. package/dist/services/oa.service.d.ts.map +1 -1
  36. package/dist/services/oa.service.js +23 -5
  37. package/dist/services/oa.service.js.map +1 -1
  38. package/dist/services/user.service.d.ts +108 -18
  39. package/dist/services/user.service.d.ts.map +1 -1
  40. package/dist/services/user.service.js +260 -59
  41. package/dist/services/user.service.js.map +1 -1
  42. package/dist/services/video-upload.service.d.ts +1 -1
  43. package/dist/services/video-upload.service.d.ts.map +1 -1
  44. package/dist/services/video-upload.service.js +11 -8
  45. package/dist/services/video-upload.service.js.map +1 -1
  46. package/dist/services/zns.service.d.ts +88 -21
  47. package/dist/services/zns.service.d.ts.map +1 -1
  48. package/dist/services/zns.service.js +157 -42
  49. package/dist/services/zns.service.js.map +1 -1
  50. package/dist/types/group.d.ts +5 -5
  51. package/dist/types/group.d.ts.map +1 -1
  52. package/dist/types/user.d.ts +155 -12
  53. package/dist/types/user.d.ts.map +1 -1
  54. package/dist/types/user.js.map +1 -1
  55. package/dist/types/zns.d.ts +67 -33
  56. package/dist/types/zns.d.ts.map +1 -1
  57. package/dist/zalo-sdk.d.ts +3 -11
  58. package/dist/zalo-sdk.d.ts.map +1 -1
  59. package/dist/zalo-sdk.js +0 -10
  60. package/dist/zalo-sdk.js.map +1 -1
  61. package/docs/API_REFERENCE.md +680 -0
  62. package/docs/AUTHENTICATION.md +709 -0
  63. package/docs/CONSULTATION_SERVICE.md +512 -330
  64. package/docs/GROUP_MANAGEMENT.md +2 -2
  65. package/docs/MESSAGE_SERVICES.md +1224 -0
  66. package/docs/TAG_MANAGEMENT.md +1462 -0
  67. package/docs/USER_MANAGEMENT.md +481 -0
  68. package/docs/ZNS_SERVICE.md +985 -0
  69. package/package.json +1 -1
  70. package/dist/services/tag.service.d.ts +0 -144
  71. package/dist/services/tag.service.d.ts.map +0 -1
  72. package/dist/services/tag.service.js +0 -184
  73. package/dist/services/tag.service.js.map +0 -1
  74. package/dist/services/user-management.service.d.ts +0 -117
  75. package/dist/services/user-management.service.d.ts.map +0 -1
  76. package/dist/services/user-management.service.js +0 -239
  77. package/dist/services/user-management.service.js.map +0 -1
@@ -0,0 +1,709 @@
1
+ # RedAI Zalo SDK - Authentication Guide
2
+
3
+ ## Tổng quan
4
+
5
+ RedAI Zalo SDK hỗ trợ đầy đủ các authentication flows của Zalo, bao gồm:
6
+
7
+ - **Official Account (OA) Authentication** - Để truy cập OA APIs
8
+ - **Social API Authentication** - Để truy cập thông tin user social
9
+ - **Token Management** - Refresh và validate tokens
10
+ - **PKCE Support** - Security enhancement cho Social API
11
+
12
+ ---
13
+
14
+ ## Official Account Authentication
15
+
16
+ ### 1. Tạo Authorization URL
17
+
18
+ ```typescript
19
+ import { ZaloSDK } from "@warriorteam/redai-zalo-sdk";
20
+
21
+ const zalo = new ZaloSDK({
22
+ appId: "your-oa-app-id",
23
+ appSecret: "your-oa-app-secret"
24
+ });
25
+
26
+ // Tạo authorization URL cho OA
27
+ const authUrl = zalo.createOAAuthUrl(
28
+ "https://your-app.com/auth/callback", // redirect_uri
29
+ "optional-state-parameter" // state (tùy chọn)
30
+ );
31
+
32
+ console.log("Redirect user to:", authUrl);
33
+ // Output: https://oauth.zaloapp.com/v4/oa/permission?app_id=xxx&redirect_uri=xxx&state=xxx
34
+ ```
35
+
36
+ ### 2. Xử lý Callback và Lấy Access Token
37
+
38
+ ```typescript
39
+ // Trong route callback của bạn
40
+ app.get('/auth/callback', async (req, res) => {
41
+ const { code, state } = req.query;
42
+
43
+ try {
44
+ // Lấy access token từ authorization code
45
+ const tokenResponse = await zalo.getOAAccessToken(
46
+ code as string,
47
+ "https://your-app.com/auth/callback"
48
+ );
49
+
50
+ console.log("OA Access Token:", tokenResponse.access_token);
51
+ console.log("Refresh Token:", tokenResponse.refresh_token);
52
+ console.log("Expires In:", tokenResponse.expires_in); // seconds
53
+
54
+ // Lưu tokens vào database/session
55
+ await saveTokens(tokenResponse);
56
+
57
+ res.redirect('/dashboard');
58
+ } catch (error) {
59
+ console.error("Auth error:", error);
60
+ res.redirect('/auth/error');
61
+ }
62
+ });
63
+ ```
64
+
65
+ ### 3. Token Response Structure
66
+
67
+ ```typescript
68
+ interface AccessToken {
69
+ access_token: string; // Token để gọi API
70
+ refresh_token: string; // Token để refresh
71
+ expires_in: number; // Thời gian sống (seconds)
72
+ token_type: "Bearer"; // Loại token
73
+ scope: string; // Quyền được cấp
74
+ }
75
+ ```
76
+
77
+ ### 4. Sử dụng Access Token
78
+
79
+ ```typescript
80
+ // Lấy thông tin OA
81
+ const oaInfo = await zalo.getOAInfo(tokenResponse.access_token);
82
+ console.log("OA Name:", oaInfo.name);
83
+ console.log("Followers:", oaInfo.num_follower);
84
+
85
+ // Gửi tin nhắn consultation
86
+ await zalo.sendConsultationText(
87
+ tokenResponse.access_token,
88
+ "user-zalo-id",
89
+ "Xin chào! Cảm ơn bạn đã quan tâm đến dịch vụ của chúng tôi."
90
+ );
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Social API Authentication
96
+
97
+ ### 1. Tạo Authorization URL (Basic)
98
+
99
+ ```typescript
100
+ const zalo = new ZaloSDK({
101
+ appId: "your-social-app-id",
102
+ appSecret: "your-social-app-secret"
103
+ });
104
+
105
+ // Tạo authorization URL cho Social API
106
+ const authUrl = zalo.createSocialAuthUrl(
107
+ "https://your-app.com/social/callback",
108
+ "optional-state"
109
+ );
110
+
111
+ console.log("Redirect user to:", authUrl);
112
+ ```
113
+
114
+ ### 2. Tạo Authorization URL với PKCE (Khuyến nghị)
115
+
116
+ ```typescript
117
+ // Tạo PKCE parameters cho bảo mật cao hơn
118
+ const pkce = zalo.generatePKCE();
119
+ console.log("Code Verifier:", pkce.code_verifier);
120
+ console.log("Code Challenge:", pkce.code_challenge);
121
+
122
+ // Lưu code_verifier vào session/state
123
+ req.session.codeVerifier = pkce.code_verifier;
124
+
125
+ const authUrl = zalo.createSocialAuthUrl(
126
+ "https://your-app.com/social/callback",
127
+ "social-login"
128
+ );
129
+ ```
130
+
131
+ ### 3. PKCE Generation
132
+
133
+ ```typescript
134
+ interface PKCEConfig {
135
+ code_verifier: string; // Random string 43-128 chars
136
+ code_challenge: string; // base64url(sha256(code_verifier))
137
+ code_challenge_method: "S256";
138
+ }
139
+
140
+ // SDK tự động tạo PKCE theo chuẩn RFC 7636
141
+ const pkce = zalo.generatePKCE();
142
+ ```
143
+
144
+ ### 4. Xử lý Social Callback
145
+
146
+ ```typescript
147
+ app.get('/social/callback', async (req, res) => {
148
+ const { code, state } = req.query;
149
+ const codeVerifier = req.session.codeVerifier; // Lấy từ session
150
+
151
+ try {
152
+ const tokenResponse = await zalo.getSocialAccessToken(
153
+ code as string,
154
+ "https://your-app.com/social/callback",
155
+ codeVerifier // Bắt buộc nếu dùng PKCE
156
+ );
157
+
158
+ // Lấy thông tin user
159
+ const userInfo = await zalo.getSocialUserInfo(
160
+ tokenResponse.access_token,
161
+ "id,name,picture,birthday,gender" // fields cần lấy
162
+ );
163
+
164
+ console.log("User Info:", userInfo);
165
+
166
+ // Lưu vào database
167
+ await createOrUpdateUser(userInfo, tokenResponse);
168
+
169
+ res.redirect('/profile');
170
+ } catch (error) {
171
+ console.error("Social auth error:", error);
172
+ res.redirect('/login?error=auth_failed');
173
+ }
174
+ });
175
+ ```
176
+
177
+ ### 5. Social User Info Response
178
+
179
+ ```typescript
180
+ interface SocialUserInfo {
181
+ id: string; // Zalo user ID
182
+ name: string; // Tên hiển thị
183
+ picture?: {
184
+ data: {
185
+ url: string; // Avatar URL
186
+ }
187
+ };
188
+ birthday?: string; // Ngày sinh (YYYY-MM-DD)
189
+ gender?: number; // 1: Nam, 2: Nữ
190
+ locale?: string; // Locale
191
+ // Các fields khác tùy theo quyền được cấp
192
+ }
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Token Management
198
+
199
+ ### 1. Refresh OA Access Token
200
+
201
+ ```typescript
202
+ async function refreshOAToken(refreshToken: string): Promise<AccessToken> {
203
+ try {
204
+ const newTokens = await zalo.refreshOAAccessToken(refreshToken);
205
+
206
+ // Lưu tokens mới
207
+ await updateTokens(newTokens);
208
+
209
+ return newTokens;
210
+ } catch (error) {
211
+ console.error("Failed to refresh OA token:", error);
212
+ // Redirect to re-authentication
213
+ throw new Error("Re-authentication required");
214
+ }
215
+ }
216
+ ```
217
+
218
+ ### 2. Refresh Social Access Token
219
+
220
+ ```typescript
221
+ async function refreshSocialToken(refreshToken: string): Promise<AccessToken> {
222
+ try {
223
+ const newTokens = await zalo.refreshSocialAccessToken(refreshToken);
224
+ return newTokens;
225
+ } catch (error) {
226
+ console.error("Failed to refresh social token:", error);
227
+ throw error;
228
+ }
229
+ }
230
+ ```
231
+
232
+ ### 3. Auto Token Refresh Middleware
233
+
234
+ ```typescript
235
+ // Express middleware để auto refresh tokens
236
+ export const autoRefreshToken = async (req: Request, res: Response, next: NextFunction) => {
237
+ const { accessToken, refreshToken, expiresAt } = req.user;
238
+
239
+ // Kiểm tra token sắp hết hạn (trước 5 phút)
240
+ const willExpireSoon = Date.now() > (expiresAt - 5 * 60 * 1000);
241
+
242
+ if (willExpireSoon && refreshToken) {
243
+ try {
244
+ const tokenType = req.path.includes('/oa/') ? 'oa' : 'social';
245
+
246
+ const newTokens = tokenType === 'oa'
247
+ ? await zalo.refreshOAAccessToken(refreshToken)
248
+ : await zalo.refreshSocialAccessToken(refreshToken);
249
+
250
+ // Cập nhật user với tokens mới
251
+ req.user.accessToken = newTokens.access_token;
252
+ req.user.refreshToken = newTokens.refresh_token;
253
+ req.user.expiresAt = Date.now() + (newTokens.expires_in * 1000);
254
+
255
+ // Lưu vào database
256
+ await updateUserTokens(req.user.id, newTokens);
257
+
258
+ console.log("Token refreshed successfully");
259
+ } catch (error) {
260
+ console.error("Auto refresh failed:", error);
261
+ return res.status(401).json({ error: "Authentication expired" });
262
+ }
263
+ }
264
+
265
+ next();
266
+ };
267
+ ```
268
+
269
+ ---
270
+
271
+ ## Token Validation
272
+
273
+ ### 1. Validate Access Token
274
+
275
+ ```typescript
276
+ // Validate OA token
277
+ const isValidOA = await zalo.validateAccessToken(accessToken, 'oa');
278
+ console.log("OA Token valid:", isValidOA);
279
+
280
+ // Validate Social token
281
+ const isValidSocial = await zalo.validateAccessToken(accessToken, 'social');
282
+ console.log("Social Token valid:", isValidSocial);
283
+ ```
284
+
285
+ ### 2. Advanced Token Validation
286
+
287
+ ```typescript
288
+ // Sử dụng service trực tiếp để có thêm thông tin
289
+ const validation = await zalo.auth.validateAccessToken(accessToken);
290
+
291
+ interface TokenValidation {
292
+ valid: boolean;
293
+ expires_in?: number; // Thời gian còn lại (seconds)
294
+ scope?: string; // Quyền hiện tại
295
+ app_id?: string; // App ID của token
296
+ user_id?: string; // User ID (nếu có)
297
+ }
298
+ ```
299
+
300
+ ---
301
+
302
+ ## Security Best Practices
303
+
304
+ ### 1. State Parameter
305
+
306
+ Luôn sử dụng `state` parameter để chống CSRF:
307
+
308
+ ```typescript
309
+ // Tạo random state
310
+ const state = crypto.randomBytes(16).toString('hex');
311
+ req.session.authState = state;
312
+
313
+ const authUrl = zalo.createOAAuthUrl(redirectUri, state);
314
+
315
+ // Trong callback, verify state
316
+ if (req.query.state !== req.session.authState) {
317
+ throw new Error("Invalid state parameter");
318
+ }
319
+ ```
320
+
321
+ ### 2. PKCE cho Social API
322
+
323
+ Luôn sử dụng PKCE cho Social API:
324
+
325
+ ```typescript
326
+ // ✅ Đúng - với PKCE
327
+ const pkce = zalo.generatePKCE();
328
+ req.session.codeVerifier = pkce.code_verifier;
329
+
330
+ // ❌ Sai - không dùng PKCE
331
+ const authUrl = zalo.createSocialAuthUrl(redirectUri);
332
+ ```
333
+
334
+ ### 3. Token Storage
335
+
336
+ ```typescript
337
+ // ✅ Đúng - mã hóa tokens khi lưu
338
+ const encryptedToken = encrypt(accessToken);
339
+ await db.users.update(userId, {
340
+ access_token: encryptedToken,
341
+ refresh_token: encrypt(refreshToken)
342
+ });
343
+
344
+ // ❌ Sai - lưu plain text
345
+ await db.users.update(userId, {
346
+ access_token: accessToken // Không an toàn
347
+ });
348
+ ```
349
+
350
+ ### 4. Token Scope Validation
351
+
352
+ ```typescript
353
+ // Kiểm tra token có đúng scope không
354
+ async function requireScope(requiredScope: string) {
355
+ const validation = await zalo.auth.validateAccessToken(accessToken);
356
+
357
+ if (!validation.scope?.includes(requiredScope)) {
358
+ throw new Error(`Missing required scope: ${requiredScope}`);
359
+ }
360
+ }
361
+
362
+ // Usage
363
+ await requireScope('oa.message.send');
364
+ ```
365
+
366
+ ---
367
+
368
+ ## Error Handling
369
+
370
+ ### 1. Common Auth Errors
371
+
372
+ ```typescript
373
+ try {
374
+ const tokens = await zalo.getOAAccessToken(code, redirectUri);
375
+ } catch (error) {
376
+ if (error.code === -201) {
377
+ // Invalid parameters (code expired, wrong redirect_uri, etc.)
378
+ console.error("Invalid auth parameters:", error.message);
379
+ } else if (error.code === -216) {
380
+ // Invalid app credentials
381
+ console.error("Invalid app_id or app_secret");
382
+ } else {
383
+ console.error("Unexpected auth error:", error);
384
+ }
385
+ }
386
+ ```
387
+
388
+ ### 2. Token Refresh Errors
389
+
390
+ ```typescript
391
+ async function handleTokenRefresh(refreshToken: string) {
392
+ try {
393
+ return await zalo.refreshOAAccessToken(refreshToken);
394
+ } catch (error) {
395
+ if (error.code === -216) {
396
+ // Refresh token expired/invalid
397
+ console.log("Refresh token expired, require re-authentication");
398
+ redirectToLogin();
399
+ } else {
400
+ console.error("Refresh failed:", error);
401
+ throw error;
402
+ }
403
+ }
404
+ }
405
+ ```
406
+
407
+ ---
408
+
409
+ ## Complete Authentication Flow Examples
410
+
411
+ ### 1. OA Authentication với Express
412
+
413
+ ```typescript
414
+ import express from 'express';
415
+ import { ZaloSDK } from '@warriorteam/redai-zalo-sdk';
416
+
417
+ const app = express();
418
+ const zalo = new ZaloSDK({
419
+ appId: process.env.ZALO_OA_APP_ID!,
420
+ appSecret: process.env.ZALO_OA_APP_SECRET!,
421
+ debug: process.env.NODE_ENV === 'development'
422
+ });
423
+
424
+ // Route bắt đầu auth
425
+ app.get('/auth/oa', (req, res) => {
426
+ const state = generateRandomState();
427
+ req.session.authState = state;
428
+
429
+ const authUrl = zalo.createOAAuthUrl(
430
+ `${process.env.BASE_URL}/auth/oa/callback`,
431
+ state
432
+ );
433
+
434
+ res.redirect(authUrl);
435
+ });
436
+
437
+ // Callback handler
438
+ app.get('/auth/oa/callback', async (req, res) => {
439
+ try {
440
+ const { code, state, error } = req.query;
441
+
442
+ if (error) {
443
+ return res.redirect(`/auth/error?reason=${error}`);
444
+ }
445
+
446
+ if (state !== req.session.authState) {
447
+ return res.status(400).json({ error: 'Invalid state' });
448
+ }
449
+
450
+ const tokens = await zalo.getOAAccessToken(
451
+ code as string,
452
+ `${process.env.BASE_URL}/auth/oa/callback`
453
+ );
454
+
455
+ // Lấy thông tin OA
456
+ const oaInfo = await zalo.getOAInfo(tokens.access_token);
457
+
458
+ // Lưu vào database
459
+ const oaAccount = await OAAccount.create({
460
+ oa_id: oaInfo.oa_id,
461
+ name: oaInfo.name,
462
+ access_token: encrypt(tokens.access_token),
463
+ refresh_token: encrypt(tokens.refresh_token),
464
+ expires_at: new Date(Date.now() + tokens.expires_in * 1000),
465
+ scope: tokens.scope
466
+ });
467
+
468
+ req.session.oaId = oaAccount.id;
469
+ res.redirect('/oa/dashboard');
470
+
471
+ } catch (error) {
472
+ console.error('OA Auth error:', error);
473
+ res.redirect('/auth/error');
474
+ }
475
+ });
476
+ ```
477
+
478
+ ### 2. Social Authentication với Next.js
479
+
480
+ ```typescript
481
+ // pages/api/auth/social/login.ts
482
+ import type { NextApiRequest, NextApiResponse } from 'next';
483
+ import { ZaloSDK } from '@warriorteam/redai-zalo-sdk';
484
+
485
+ const zalo = new ZaloSDK({
486
+ appId: process.env.ZALO_SOCIAL_APP_ID!,
487
+ appSecret: process.env.ZALO_SOCIAL_APP_SECRET!
488
+ });
489
+
490
+ export default async function handler(req: NextApiRequest, res: NextApiResponse) {
491
+ if (req.method !== 'GET') {
492
+ return res.status(405).json({ error: 'Method not allowed' });
493
+ }
494
+
495
+ // Generate PKCE
496
+ const pkce = zalo.generatePKCE();
497
+ const state = generateRandomState();
498
+
499
+ // Lưu vào session/cookie (hoặc Redis)
500
+ res.setHeader('Set-Cookie', [
501
+ `pkce_verifier=${pkce.code_verifier}; HttpOnly; Secure; SameSite=Strict`,
502
+ `auth_state=${state}; HttpOnly; Secure; SameSite=Strict`
503
+ ]);
504
+
505
+ const authUrl = zalo.createSocialAuthUrl(
506
+ `${process.env.NEXTAUTH_URL}/api/auth/social/callback`,
507
+ state
508
+ );
509
+
510
+ res.redirect(authUrl);
511
+ }
512
+
513
+ // pages/api/auth/social/callback.ts
514
+ export default async function handler(req: NextApiRequest, res: NextApiResponse) {
515
+ try {
516
+ const { code, state } = req.query;
517
+ const cookies = parseCookies(req.headers.cookie || '');
518
+
519
+ if (state !== cookies.auth_state) {
520
+ throw new Error('Invalid state');
521
+ }
522
+
523
+ const tokens = await zalo.getSocialAccessToken(
524
+ code as string,
525
+ `${process.env.NEXTAUTH_URL}/api/auth/social/callback`,
526
+ cookies.pkce_verifier
527
+ );
528
+
529
+ const userInfo = await zalo.getSocialUserInfo(
530
+ tokens.access_token,
531
+ 'id,name,picture,birthday'
532
+ );
533
+
534
+ // Tạo hoặc cập nhật user
535
+ const user = await User.upsert({
536
+ zalo_id: userInfo.id,
537
+ name: userInfo.name,
538
+ avatar: userInfo.picture?.data.url,
539
+ birthday: userInfo.birthday
540
+ });
541
+
542
+ // Lưu tokens
543
+ await UserToken.create({
544
+ user_id: user.id,
545
+ access_token: encrypt(tokens.access_token),
546
+ refresh_token: encrypt(tokens.refresh_token),
547
+ expires_at: new Date(Date.now() + tokens.expires_in * 1000),
548
+ token_type: 'social'
549
+ });
550
+
551
+ // Tạo session
552
+ const sessionToken = jwt.sign(
553
+ { userId: user.id, zaloId: userInfo.id },
554
+ process.env.JWT_SECRET!,
555
+ { expiresIn: '7d' }
556
+ );
557
+
558
+ res.setHeader('Set-Cookie', `session=${sessionToken}; HttpOnly; Secure`);
559
+ res.redirect('/profile');
560
+
561
+ } catch (error) {
562
+ console.error('Social auth error:', error);
563
+ res.redirect('/login?error=auth_failed');
564
+ }
565
+ }
566
+ ```
567
+
568
+ ---
569
+
570
+ ## Environment Configuration
571
+
572
+ ```typescript
573
+ // .env file
574
+ ZALO_OA_APP_ID=your_oa_app_id
575
+ ZALO_OA_APP_SECRET=your_oa_app_secret
576
+ ZALO_SOCIAL_APP_ID=your_social_app_id
577
+ ZALO_SOCIAL_APP_SECRET=your_social_app_secret
578
+
579
+ // config.ts
580
+ export const zaloConfig = {
581
+ oa: {
582
+ appId: process.env.ZALO_OA_APP_ID!,
583
+ appSecret: process.env.ZALO_OA_APP_SECRET!,
584
+ },
585
+ social: {
586
+ appId: process.env.ZALO_SOCIAL_APP_ID!,
587
+ appSecret: process.env.ZALO_SOCIAL_APP_SECRET!,
588
+ }
589
+ };
590
+
591
+ // Khởi tạo SDK instances
592
+ export const zaloOA = new ZaloSDK(zaloConfig.oa);
593
+ export const zaloSocial = new ZaloSDK(zaloConfig.social);
594
+ ```
595
+
596
+ ---
597
+
598
+ ## Testing Authentication
599
+
600
+ ### 1. Unit Tests
601
+
602
+ ```typescript
603
+ // auth.test.ts
604
+ import { ZaloSDK } from '@warriorteam/redai-zalo-sdk';
605
+
606
+ describe('Authentication', () => {
607
+ const zalo = new ZaloSDK({
608
+ appId: 'test_app_id',
609
+ appSecret: 'test_app_secret'
610
+ });
611
+
612
+ it('should create OA auth URL', () => {
613
+ const url = zalo.createOAAuthUrl('http://localhost:3000/callback', 'test-state');
614
+ expect(url).toContain('oauth.zaloapp.com/v4/oa/permission');
615
+ expect(url).toContain('app_id=test_app_id');
616
+ expect(url).toContain('state=test-state');
617
+ });
618
+
619
+ it('should generate valid PKCE', () => {
620
+ const pkce = zalo.generatePKCE();
621
+ expect(pkce.code_verifier).toHaveLength(128);
622
+ expect(pkce.code_challenge_method).toBe('S256');
623
+ expect(pkce.code_challenge).toBeDefined();
624
+ });
625
+ });
626
+ ```
627
+
628
+ ### 2. Integration Tests
629
+
630
+ ```typescript
631
+ // auth.integration.test.ts
632
+ describe('Authentication Integration', () => {
633
+ it('should complete OA auth flow', async () => {
634
+ // Sử dụng test credentials
635
+ const testCode = 'test_authorization_code';
636
+ const testRedirectUri = 'http://localhost:3000/callback';
637
+
638
+ const tokens = await zalo.getOAAccessToken(testCode, testRedirectUri);
639
+
640
+ expect(tokens.access_token).toBeDefined();
641
+ expect(tokens.refresh_token).toBeDefined();
642
+ expect(tokens.expires_in).toBeGreaterThan(0);
643
+ });
644
+ });
645
+ ```
646
+
647
+ ---
648
+
649
+ ## Troubleshooting
650
+
651
+ ### 1. Common Issues
652
+
653
+ **Q: "Invalid redirect_uri" error**
654
+ ```
655
+ A: Đảm bảo redirect_uri trong callback chính xác giống với lúc tạo auth URL
656
+ và đã được đăng ký trong Zalo Developer Console
657
+ ```
658
+
659
+ **Q: "Invalid code" error**
660
+ ```
661
+ A: Authorization code chỉ dùng được 1 lần và có thời gian sống ngắn (~10 phút)
662
+ Đảm bảo xử lý callback ngay sau khi user authorize
663
+ ```
664
+
665
+ **Q: "Invalid app_id or app_secret"**
666
+ ```
667
+ A: Kiểm tra credentials trong Zalo Developer Console
668
+ Đảm bảo dùng đúng app_id/app_secret cho môi trường (dev/prod)
669
+ ```
670
+
671
+ ### 2. Debug Authentication
672
+
673
+ ```typescript
674
+ // Enable debug logging
675
+ const zalo = new ZaloSDK({
676
+ appId: 'your_app_id',
677
+ appSecret: 'your_app_secret',
678
+ debug: true // Bật debug logs
679
+ });
680
+
681
+ // Manual token validation
682
+ async function debugToken(accessToken: string) {
683
+ try {
684
+ const validation = await zalo.auth.validateAccessToken(accessToken);
685
+ console.log('Token validation:', validation);
686
+
687
+ if (validation.valid) {
688
+ console.log(`Token expires in: ${validation.expires_in} seconds`);
689
+ console.log(`Token scope: ${validation.scope}`);
690
+ }
691
+ } catch (error) {
692
+ console.error('Token validation failed:', error);
693
+ }
694
+ }
695
+ ```
696
+
697
+ ---
698
+
699
+ ## Next Steps
700
+
701
+ Sau khi hoàn thành authentication:
702
+
703
+ 1. **[ZNS Service](./ZNS_SERVICE.md)** - Gửi notification messages
704
+ 2. **[Message Services](./MESSAGE_SERVICES.md)** - Gửi các loại tin nhắn khác nhau
705
+ 3. **[User Management](./USER_MANAGEMENT.md)** - Quản lý user profiles
706
+ 4. **[Group Management](./GROUP_MANAGEMENT.md)** - Quản lý Zalo groups
707
+ 5. **[Webhook Integration](./WEBHOOK_EVENTS.md)** - Xử lý real-time events
708
+
709
+ Tham khảo **[API Reference](./API_REFERENCE.md)** để biết chi tiết về tất cả methods có sẵn.