@cabin-id/nextjs 1.2.5 → 1.2.6

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 (2) hide show
  1. package/README.md +579 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,579 @@
1
+ # CabinID SDK
2
+
3
+ CabinID SDK là một bộ công cụ xác thực và quản lý danh tính đa cấp, cung cấp giải pháp tích hợp dễ dàng cho các ứng dụng Next.js và React.
4
+
5
+ ## 📦 Cài đặt
6
+
7
+ ```bash
8
+ npm install @cabin-id/nextjs
9
+ # hoặc
10
+ pnpm add @cabin-id/nextjs
11
+ # hoặc
12
+ yarn add @cabin-id/nextjs
13
+ ```
14
+
15
+ ## 🚀 Cấu hình nhanh
16
+
17
+ ### 1. Thiết lập biến môi trường
18
+
19
+ Tạo file `.env.local` trong thư mục gốc dự án của bạn:
20
+
21
+ ```bash
22
+ # CabinID Configuration
23
+ NEXT_PUBLIC_CABIN_ID_PUBLISH_KEY=cabin_pk_your_publishable_key_here
24
+ CABIN_ID_SECRET_KEY=cabin_sk_your_secret_key_here
25
+
26
+ # URL Redirects
27
+ NEXT_PUBLIC_CABIN_ID_AFTER_SIGN_IN_URL=/dashboard
28
+ NEXT_PUBLIC_CABIN_ID_AFTER_SIGN_UP_URL=/welcome
29
+ NEXT_PUBLIC_CABIN_ID_SIGN_IN_URL=/auth
30
+ ```
31
+
32
+ ### 2. Cấu hình CabinIdProvider
33
+
34
+ Bọc ứng dụng của bạn với `CabinIdProvider`:
35
+
36
+ ```tsx
37
+ // app/layout.tsx hoặc pages/_app.tsx
38
+ 'use client';
39
+
40
+ import { CabinIdProvider } from '@cabin-id/nextjs';
41
+
42
+ export default function RootLayout({
43
+ children,
44
+ }: {
45
+ children: React.ReactNode;
46
+ }) {
47
+ return (
48
+ <html lang="vi">
49
+ <body>
50
+ <CabinIdProvider>
51
+ {children}
52
+ </CabinIdProvider>
53
+ </body>
54
+ </html>
55
+ );
56
+ }
57
+ ```
58
+
59
+ ### 3. Middleware (Tùy chọn)
60
+
61
+ Tạo file `middleware.ts` trong thư mục gốc để bảo vệ các route:
62
+
63
+ ```tsx
64
+ // middleware.ts
65
+ import { authMiddleware } from '@cabin-id/nextjs/server';
66
+
67
+ export default authMiddleware({
68
+ publicRoutes: ['/'],
69
+ afterAuth(auth, req) {
70
+ // Xử lý sau khi xác thực
71
+ if (!auth.userId && !auth.isPublicRoute) {
72
+ return Response.redirect(new URL('/auth', req.url));
73
+ }
74
+ },
75
+ });
76
+
77
+ export const config = {
78
+ matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
79
+ };
80
+ ```
81
+
82
+ ## 🔐 Tích hợp Authentication
83
+
84
+ ### SignInButton - Nút Đăng nhập
85
+
86
+ ```tsx
87
+ 'use client';
88
+
89
+ import { SignInButton } from '@cabin-id/nextjs';
90
+
91
+ export default function LoginPage() {
92
+ return (
93
+ <div className="flex items-center justify-center min-h-screen">
94
+ <div className="max-w-md w-full space-y-8">
95
+ <div>
96
+ <h2 className="text-center text-3xl font-extrabold text-gray-900">
97
+ Đăng nhập vào tài khoản
98
+ </h2>
99
+ </div>
100
+
101
+ <SignInButton />
102
+ </div>
103
+ </div>
104
+ );
105
+ }
106
+ ```
107
+
108
+ ### SignOutButton - Nút Đăng xuất
109
+
110
+ ```tsx
111
+ 'use client';
112
+
113
+ import { SignOutButton } from '@cabin-id/nextjs';
114
+
115
+ export default function Dashboard() {
116
+ return (
117
+ <div className="min-h-screen bg-gray-50">
118
+ <nav className="bg-white shadow">
119
+ <div className="max-w-7xl mx-auto px-4">
120
+ <div className="flex justify-between h-16">
121
+ <div className="flex items-center">
122
+ <h1 className="text-xl font-semibold">Dashboard</h1>
123
+ </div>
124
+ <div className="flex items-center">
125
+ <SignOutButton
126
+ className="rounded bg-red-600 px-4 py-2 text-white hover:bg-red-700"
127
+ onSignOutSuccess={() => {
128
+ console.log('Đăng xuất thành công');
129
+ }}
130
+ onSignOutError={(error) => {
131
+ console.error('Lỗi đăng xuất:', error);
132
+ }}
133
+ >
134
+ Đăng xuất
135
+ </SignOutButton>
136
+ </div>
137
+ </div>
138
+ </div>
139
+ </nav>
140
+
141
+ {/* Nội dung dashboard */}
142
+ </div>
143
+ );
144
+ }
145
+ ```
146
+
147
+ ### useUser Hook - Quản lý trạng thái người dùng
148
+
149
+ ```tsx
150
+ 'use client';
151
+
152
+ import { useUser } from '@cabin-id/nextjs';
153
+
154
+ export default function ProfilePage() {
155
+ const { user, isLoaded, isSignedIn } = useUser();
156
+
157
+ if (!isLoaded) {
158
+ return <div>Đang tải...</div>;
159
+ }
160
+
161
+ if (!isSignedIn) {
162
+ return <div>Vui lòng đăng nhập để tiếp tục</div>;
163
+ }
164
+
165
+ return (
166
+ <div className="max-w-4xl mx-auto p-6">
167
+ <h1 className="text-2xl font-bold mb-4">Thông tin cá nhân</h1>
168
+
169
+ <div className="bg-white shadow rounded-lg p-6">
170
+ <div className="space-y-4">
171
+ <div>
172
+ <label className="block text-sm font-medium text-gray-700">
173
+ ID người dùng
174
+ </label>
175
+ <p className="mt-1 text-sm text-gray-900">{user.id}</p>
176
+ </div>
177
+
178
+ <div>
179
+ <label className="block text-sm font-medium text-gray-700">
180
+ Email
181
+ </label>
182
+ <p className="mt-1 text-sm text-gray-900">{user.primaryEmailAddress?.emailAddress}</p>
183
+ </div>
184
+
185
+ <div>
186
+ <label className="block text-sm font-medium text-gray-700">
187
+ Số điện thoại
188
+ </label>
189
+ <p className="mt-1 text-sm text-gray-900">{user.primaryPhoneNumber?.phoneNumber}</p>
190
+ </div>
191
+
192
+ <div>
193
+ <label className="block text-sm font-medium text-gray-700">
194
+ Ngày tạo
195
+ </label>
196
+ <p className="mt-1 text-sm text-gray-900">{user.createdAt}</p>
197
+ </div>
198
+ </div>
199
+ </div>
200
+ </div>
201
+ );
202
+ }
203
+ ```
204
+
205
+ ## 🌐 Tích hợp cho Website Bên ngoài
206
+
207
+ ### Ví dụ: Tích hợp vào website hiện có
208
+
209
+ ```tsx
210
+ // components/AuthButton.tsx
211
+ 'use client';
212
+
213
+ import { useUser, SignInButton, SignOutButton } from '@cabin-id/nextjs';
214
+
215
+ export default function AuthButton() {
216
+ const { isSignedIn, user, isLoaded } = useUser();
217
+
218
+ if (!isLoaded) {
219
+ return (
220
+ <div className="animate-pulse">
221
+ <div className="h-10 w-24 bg-gray-200 rounded"></div>
222
+ </div>
223
+ );
224
+ }
225
+
226
+ if (!isSignedIn) {
227
+ return <SignInButton />;
228
+ }
229
+
230
+ return (
231
+ <div className="flex items-center space-x-4">
232
+ <span className="text-sm text-gray-700">
233
+ Xin chào, {user.firstName || 'Người dùng'}
234
+ </span>
235
+ <SignOutButton
236
+ className="rounded bg-red-600 px-4 py-2 text-white hover:bg-red-700"
237
+ onSignOutSuccess={() => {
238
+ // Có thể thêm logic tùy chỉnh sau khi đăng xuất
239
+ window.location.reload();
240
+ }}
241
+ >
242
+ Đăng xuất
243
+ </SignOutButton>
244
+ </div>
245
+ );
246
+ }
247
+ ```
248
+
249
+ ### Bảo vệ các trang riêng tư
250
+
251
+ ```tsx
252
+ // components/ProtectedRoute.tsx
253
+ 'use client';
254
+
255
+ import { useUser } from '@cabin-id/nextjs';
256
+ import { useRouter } from 'next/navigation';
257
+ import { useEffect } from 'react';
258
+
259
+ interface ProtectedRouteProps {
260
+ children: React.ReactNode;
261
+ fallback?: React.ReactNode;
262
+ }
263
+
264
+ export default function ProtectedRoute({
265
+ children,
266
+ fallback = <div>Đang chuyển hướng...</div>
267
+ }: ProtectedRouteProps) {
268
+ const { isSignedIn, isLoaded } = useUser();
269
+ const router = useRouter();
270
+
271
+ useEffect(() => {
272
+ if (isLoaded && !isSignedIn) {
273
+ router.push('/auth');
274
+ }
275
+ }, [isLoaded, isSignedIn, router]);
276
+
277
+ if (!isLoaded) {
278
+ return <div>Đang tải...</div>;
279
+ }
280
+
281
+ if (!isSignedIn) {
282
+ return fallback;
283
+ }
284
+
285
+ return <>{children}</>;
286
+ }
287
+ ```
288
+
289
+ Sử dụng:
290
+
291
+ ```tsx
292
+ // app/dashboard/page.tsx
293
+ import ProtectedRoute from '@/components/ProtectedRoute';
294
+
295
+ export default function DashboardPage() {
296
+ return (
297
+ <ProtectedRoute>
298
+ <div className="p-6">
299
+ <h1 className="text-2xl font-bold">Dashboard</h1>
300
+ <p>Đây là trang chỉ dành cho người dùng đã đăng nhập.</p>
301
+ </div>
302
+ </ProtectedRoute>
303
+ );
304
+ }
305
+ ```
306
+
307
+ ## 🛠️ Server-side Functions
308
+
309
+ ### getCurrentUser - Lấy thông tin người dùng từ server
310
+
311
+ ```tsx
312
+ // app/api/profile/route.ts
313
+ import { getCurrentUser } from '@cabin-id/nextjs/server';
314
+ import { NextResponse } from 'next/server';
315
+
316
+ export async function GET() {
317
+ try {
318
+ const user = await getCurrentUser();
319
+
320
+ if (!user) {
321
+ return NextResponse.json(
322
+ { error: 'Chưa đăng nhập' },
323
+ { status: 401 }
324
+ );
325
+ }
326
+
327
+ return NextResponse.json({ user });
328
+ } catch (error) {
329
+ return NextResponse.json(
330
+ { error: 'Lỗi server' },
331
+ { status: 500 }
332
+ );
333
+ }
334
+ }
335
+ ```
336
+
337
+ ### protect - Bảo vệ API routes
338
+
339
+ ```tsx
340
+ // app/api/admin/route.ts
341
+ import { protect } from '@cabin-id/nextjs/server';
342
+ import { NextResponse } from 'next/server';
343
+
344
+ export const GET = protect(async (req, { user }) => {
345
+ // Chỉ người dùng đã đăng nhập mới có thể truy cập
346
+ return NextResponse.json({
347
+ message: `Xin chào ${user.firstName}!`,
348
+ adminData: 'Dữ liệu quan trọng'
349
+ });
350
+ });
351
+ ```
352
+
353
+ ## 🎨 Tùy chỉnh CSS
354
+
355
+ ### Tùy chỉnh SignInButton
356
+
357
+ ```tsx
358
+ import { SignInButton } from '@cabin-id/nextjs';
359
+
360
+ export default function CustomSignIn() {
361
+ return (
362
+ <SignInButton
363
+ className="w-full flex justify-center py-3 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
364
+ />
365
+ );
366
+ }
367
+ ```
368
+
369
+ ### Tùy chỉnh SignOutButton
370
+
371
+ ```tsx
372
+ import { SignOutButton } from '@cabin-id/nextjs';
373
+
374
+ export default function CustomSignOut() {
375
+ return (
376
+ <SignOutButton
377
+ className="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
378
+ showLoading={true}
379
+ loadingText="Đang đăng xuất..."
380
+ onSignOutStart={() => console.log('Bắt đầu đăng xuất')}
381
+ onSignOutSuccess={() => console.log('Đăng xuất thành công')}
382
+ onSignOutError={(error) => console.error('Lỗi đăng xuất:', error)}
383
+ >
384
+ <svg className="mr-2 -ml-1 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
385
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
386
+ </svg>
387
+ Đăng xuất
388
+ </SignOutButton>
389
+ );
390
+ }
391
+ ```
392
+
393
+ ## 📝 Ví dụ hoàn chỉnh - Website Thương mại điện tử
394
+
395
+ ```tsx
396
+ // app/layout.tsx
397
+ 'use client';
398
+
399
+ import { CabinIdProvider } from '@cabin-id/nextjs';
400
+ import './globals.css';
401
+
402
+ export default function RootLayout({
403
+ children,
404
+ }: {
405
+ children: React.ReactNode;
406
+ }) {
407
+ return (
408
+ <html lang="vi">
409
+ <body>
410
+ <CabinIdProvider>
411
+ <Header />
412
+ <main>{children}</main>
413
+ <Footer />
414
+ </CabinIdProvider>
415
+ </body>
416
+ </html>
417
+ );
418
+ }
419
+
420
+ // components/Header.tsx
421
+ 'use client';
422
+
423
+ import { useUser, SignInButton, SignOutButton } from '@cabin-id/nextjs';
424
+ import Link from 'next/link';
425
+
426
+ function Header() {
427
+ const { isSignedIn, user, isLoaded } = useUser();
428
+
429
+ return (
430
+ <header className="bg-white shadow">
431
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
432
+ <div className="flex justify-between h-16">
433
+ <div className="flex items-center">
434
+ <Link href="/" className="text-xl font-bold text-gray-900">
435
+ Cửa hàng của tôi
436
+ </Link>
437
+ </div>
438
+
439
+ <nav className="hidden md:flex items-center space-x-8">
440
+ <Link href="/products" className="text-gray-500 hover:text-gray-900">
441
+ Sản phẩm
442
+ </Link>
443
+ <Link href="/about" className="text-gray-500 hover:text-gray-900">
444
+ Về chúng tôi
445
+ </Link>
446
+ <Link href="/contact" className="text-gray-500 hover:text-gray-900">
447
+ Liên hệ
448
+ </Link>
449
+ </nav>
450
+
451
+ <div className="flex items-center space-x-4">
452
+ {!isLoaded ? (
453
+ <div className="animate-pulse h-10 w-24 bg-gray-200 rounded"></div>
454
+ ) : isSignedIn ? (
455
+ <div className="flex items-center space-x-4">
456
+ <Link
457
+ href="/profile"
458
+ className="text-gray-700 hover:text-gray-900"
459
+ >
460
+ Xin chào, {user.firstName}
461
+ </Link>
462
+ <SignOutButton
463
+ className="rounded bg-red-600 px-4 py-2 text-white hover:bg-red-700"
464
+ >
465
+ Đăng xuất
466
+ </SignOutButton>
467
+ </div>
468
+ ) : (
469
+ <SignInButton />
470
+ )}
471
+ </div>
472
+ </div>
473
+ </div>
474
+ </header>
475
+ );
476
+ }
477
+ ```
478
+
479
+ ## 🔧 Cấu hình nâng cao
480
+
481
+ ### Tùy chỉnh Domain
482
+
483
+ ```tsx
484
+ // middleware.ts
485
+ import { authMiddleware } from '@cabin-id/nextjs/server';
486
+
487
+ export default authMiddleware({
488
+ // Domain tùy chỉnh
489
+ domain: 'myapp.cabinid.dev',
490
+
491
+ // Các route không cần xác thực
492
+ publicRoutes: ['/', '/products', '/about', '/contact'],
493
+
494
+ // Các route chỉ dành cho admin
495
+ adminRoutes: ['/admin'],
496
+
497
+ // Xử lý sau khi xác thực
498
+ afterAuth(auth, req) {
499
+ const { userId, isPublicRoute, isAdminRoute } = auth;
500
+
501
+ // Chuyển hướng người dùng chưa đăng nhập
502
+ if (!userId && !isPublicRoute) {
503
+ return Response.redirect(new URL('/auth', req.url));
504
+ }
505
+
506
+ // Kiểm tra quyền admin (cần implement logic kiểm tra)
507
+ if (isAdminRoute && !isAdmin(userId)) {
508
+ return Response.redirect(new URL('/unauthorized', req.url));
509
+ }
510
+ },
511
+ });
512
+
513
+ function isAdmin(userId: string): boolean {
514
+ // Logic kiểm tra quyền admin
515
+ return false;
516
+ }
517
+ ```
518
+
519
+ ## 📚 API Reference
520
+
521
+ ### Components
522
+
523
+ - `SignInButton`: Nút đăng nhập tự động chuyển hướng đến CabinID
524
+ - `SignOutButton`: Nút đăng xuất với xử lý callback
525
+ - `CabinIdProvider`: Provider chính để quản lý trạng thái xác thực
526
+
527
+ ### Hooks
528
+
529
+ - `useUser()`: Hook để lấy thông tin người dùng hiện tại
530
+
531
+ ### Server Functions
532
+
533
+ - `getCurrentUser()`: Lấy thông tin người dùng từ server
534
+ - `protect()`: Bảo vệ API routes
535
+ - `authMiddleware()`: Middleware xác thực cho Next.js
536
+
537
+ ## 🐛 Xử lý lỗi thường gặp
538
+
539
+ ### Lỗi: "CabinIdProvider not found"
540
+
541
+ Đảm bảo bạn đã bọc ứng dụng với `CabinIdProvider`:
542
+
543
+ ```tsx
544
+ // ✅ Đúng
545
+ <CabinIdProvider>
546
+ <YourApp />
547
+ </CabinIdProvider>
548
+
549
+ // ❌ Sai - thiếu provider
550
+ <YourApp />
551
+ ```
552
+
553
+ ### Lỗi: Biến môi trường không được tìm thấy
554
+
555
+ Kiểm tra file `.env.local` và đảm bảo tên biến chính xác:
556
+
557
+ ```bash
558
+ # ✅ Đúng
559
+ NEXT_PUBLIC_CABIN_ID_PUBLISH_KEY=cabin_pk_...
560
+ CABIN_ID_SECRET_KEY=cabin_sk_...
561
+
562
+ # ❌ Sai - thiếu NEXT_PUBLIC_
563
+ CABIN_ID_PUBLISH_KEY=cabin_pk_...
564
+ ```
565
+
566
+ ## 🔗 Liên kết hữu ích
567
+
568
+ - [Trang chủ CabinID](https://cabinid.dev)
569
+ - [Dashboard CabinID](https://dashboard.cabinid.dev)
570
+ - [Tài liệu API](https://docs.cabinid.dev)
571
+ - [Báo lỗi](https://github.com/cabinvn/issues)
572
+
573
+ ## 📄 Giấy phép
574
+
575
+ MIT License - xem file [LICENSE](LICENSE) để biết thêm chi tiết.
576
+
577
+ ---
578
+
579
+ 🚀 **Bắt đầu ngay hôm nay!** Tích hợp CabinID vào ứng dụng của bạn chỉ trong vài phút.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cabin-id/nextjs",
3
3
  "description": "NextJS SDK for CabinID",
4
- "version": "1.2.5",
4
+ "version": "1.2.6",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "author": "CabinVN",