npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@cabin-id/nextjs

v1.2.7

Published

NextJS SDK for CabinID

Readme

CabinID SDK

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.

📦 Cài đặt

npm install @cabin-id/nextjs
# hoặc
pnpm add @cabin-id/nextjs
# hoặc
yarn add @cabin-id/nextjs

🚀 Cấu hình nhanh

1. Thiết lập biến môi trường

Tạo file .env.local trong thư mục gốc dự án của bạn:

# CabinID Configuration
NEXT_PUBLIC_CABIN_ID_PUBLISH_KEY=cabin_pk_your_publishable_key_here
CABIN_ID_SECRET_KEY=cabin_sk_your_secret_key_here

# URL Redirects
NEXT_PUBLIC_CABIN_ID_AFTER_SIGN_IN_URL=/dashboard
NEXT_PUBLIC_CABIN_ID_AFTER_SIGN_UP_URL=/welcome
NEXT_PUBLIC_CABIN_ID_SIGN_IN_URL=/auth

2. Cấu hình CabinIdProvider

Bọc ứng dụng của bạn với CabinIdProvider:

// app/layout.tsx hoặc pages/_app.tsx
'use client';

import { CabinIdProvider } from '@cabin-id/nextjs';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="vi">
      <body>
        <CabinIdProvider>
          {children}
        </CabinIdProvider>
      </body>
    </html>
  );
}

3. Middleware (Tùy chọn)

Tạo file middleware.ts trong thư mục gốc để bảo vệ các route:

// middleware.ts
import { authMiddleware } from '@cabin-id/nextjs/server';

export default authMiddleware({
  publicRoutes: ['/'],
  afterAuth(auth, req) {
    // Xử lý sau khi xác thực
    if (!auth.userId && !auth.isPublicRoute) {
      return Response.redirect(new URL('/auth', req.url));
    }
  },
});

export const config = {
  matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
};

🔐 Tích hợp Authentication

SignInButton - Nút Đăng nhập

'use client';

import { SignInButton } from '@cabin-id/nextjs';

export default function LoginPage() {
  return (
    <div className="flex items-center justify-center min-h-screen">
      <div className="max-w-md w-full space-y-8">
        <div>
          <h2 className="text-center text-3xl font-extrabold text-gray-900">
            Đăng nhập vào tài khoản
          </h2>
        </div>
        
        <SignInButton />
      </div>
    </div>
  );
}

SignOutButton - Nút Đăng xuất

'use client';

import { SignOutButton } from '@cabin-id/nextjs';

export default function Dashboard() {
  return (
    <div className="min-h-screen bg-gray-50">
      <nav className="bg-white shadow">
        <div className="max-w-7xl mx-auto px-4">
          <div className="flex justify-between h-16">
            <div className="flex items-center">
              <h1 className="text-xl font-semibold">Dashboard</h1>
            </div>
            <div className="flex items-center">
              <SignOutButton
                className="rounded bg-red-600 px-4 py-2 text-white hover:bg-red-700"
                onSignOutSuccess={() => {
                  console.log('Đăng xuất thành công');
                }}
                onSignOutError={(error) => {
                  console.error('Lỗi đăng xuất:', error);
                }}
              >
                Đăng xuất
              </SignOutButton>
            </div>
          </div>
        </div>
      </nav>
      
      {/* Nội dung dashboard */}
    </div>
  );
}

useUser Hook - Quản lý trạng thái người dùng

'use client';

import { useUser } from '@cabin-id/nextjs';

export default function ProfilePage() {
  const { user, isLoaded, isSignedIn } = useUser();

  if (!isLoaded) {
    return <div>Đang tải...</div>;
  }

  if (!isSignedIn) {
    return <div>Vui lòng đăng nhập để tiếp tục</div>;
  }

  return (
    <div className="max-w-4xl mx-auto p-6">
      <h1 className="text-2xl font-bold mb-4">Thông tin cá nhân</h1>
      
      <div className="bg-white shadow rounded-lg p-6">
        <div className="space-y-4">
          <div>
            <label className="block text-sm font-medium text-gray-700">
              ID người dùng
            </label>
            <p className="mt-1 text-sm text-gray-900">{user.id}</p>
          </div>
          
          <div>
            <label className="block text-sm font-medium text-gray-700">
              Email
            </label>
            <p className="mt-1 text-sm text-gray-900">{user.primaryEmailAddress?.emailAddress}</p>
          </div>
          
          <div>
            <label className="block text-sm font-medium text-gray-700">
              Số điện thoại
            </label>
            <p className="mt-1 text-sm text-gray-900">{user.primaryPhoneNumber?.phoneNumber}</p>
          </div>
          
          <div>
            <label className="block text-sm font-medium text-gray-700">
              Ngày tạo
            </label>
            <p className="mt-1 text-sm text-gray-900">{user.createdAt}</p>
          </div>
        </div>
      </div>
    </div>
  );
}

🌐 Tích hợp cho Website Bên ngoài

Ví dụ: Tích hợp vào website hiện có

// components/AuthButton.tsx
'use client';

import { useUser, SignInButton, SignOutButton } from '@cabin-id/nextjs';

export default function AuthButton() {
  const { isSignedIn, user, isLoaded } = useUser();

  if (!isLoaded) {
    return (
      <div className="animate-pulse">
        <div className="h-10 w-24 bg-gray-200 rounded"></div>
      </div>
    );
  }

  if (!isSignedIn) {
    return <SignInButton />;
  }

  return (
    <div className="flex items-center space-x-4">
      <span className="text-sm text-gray-700">
        Xin chào, {user.firstName || 'Người dùng'}
      </span>
      <SignOutButton
        className="rounded bg-red-600 px-4 py-2 text-white hover:bg-red-700"
        onSignOutSuccess={() => {
          // Có thể thêm logic tùy chỉnh sau khi đăng xuất
          window.location.reload();
        }}
      >
        Đăng xuất
      </SignOutButton>
    </div>
  );
}

Bảo vệ các trang riêng tư

// components/ProtectedRoute.tsx
'use client';

import { useUser } from '@cabin-id/nextjs';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';

interface ProtectedRouteProps {
  children: React.ReactNode;
  fallback?: React.ReactNode;
}

export default function ProtectedRoute({ 
  children, 
  fallback = <div>Đang chuyển hướng...</div> 
}: ProtectedRouteProps) {
  const { isSignedIn, isLoaded } = useUser();
  const router = useRouter();

  useEffect(() => {
    if (isLoaded && !isSignedIn) {
      router.push('/auth');
    }
  }, [isLoaded, isSignedIn, router]);

  if (!isLoaded) {
    return <div>Đang tải...</div>;
  }

  if (!isSignedIn) {
    return fallback;
  }

  return <>{children}</>;
}

Sử dụng:

// app/dashboard/page.tsx
import ProtectedRoute from '@/components/ProtectedRoute';

export default function DashboardPage() {
  return (
    <ProtectedRoute>
      <div className="p-6">
        <h1 className="text-2xl font-bold">Dashboard</h1>
        <p>Đây là trang chỉ dành cho người dùng đã đăng nhập.</p>
      </div>
    </ProtectedRoute>
  );
}

🛠️ Server-side Functions

getCurrentUser - Lấy thông tin người dùng từ server

// app/api/profile/route.ts
import { getCurrentUser } from '@cabin-id/nextjs/server';
import { NextResponse } from 'next/server';

export async function GET() {
  try {
    const user = await getCurrentUser();
    
    if (!user) {
      return NextResponse.json(
        { error: 'Chưa đăng nhập' },
        { status: 401 }
      );
    }

    return NextResponse.json({ user });
  } catch (error) {
    return NextResponse.json(
      { error: 'Lỗi server' },
      { status: 500 }
    );
  }
}

protect - Bảo vệ API routes

// app/api/admin/route.ts
import { protect } from '@cabin-id/nextjs/server';
import { NextResponse } from 'next/server';

export const GET = protect(async (req, { user }) => {
  // Chỉ người dùng đã đăng nhập mới có thể truy cập
  return NextResponse.json({
    message: `Xin chào ${user.firstName}!`,
    adminData: 'Dữ liệu quan trọng'
  });
});

🎨 Tùy chỉnh CSS

Tùy chỉnh SignInButton

import { SignInButton } from '@cabin-id/nextjs';

export default function CustomSignIn() {
  return (
    <SignInButton 
      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"
    />
  );
}

Tùy chỉnh SignOutButton

import { SignOutButton } from '@cabin-id/nextjs';

export default function CustomSignOut() {
  return (
    <SignOutButton
      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"
      showLoading={true}
      loadingText="Đang đăng xuất..."
      onSignOutStart={() => console.log('Bắt đầu đăng xuất')}
      onSignOutSuccess={() => console.log('Đăng xuất thành công')}
      onSignOutError={(error) => console.error('Lỗi đăng xuất:', error)}
    >
      <svg className="mr-2 -ml-1 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <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" />
      </svg>
      Đăng xuất
    </SignOutButton>
  );
}

📝 Ví dụ hoàn chỉnh - Website Thương mại điện tử

// app/layout.tsx
'use client';

import { CabinIdProvider } from '@cabin-id/nextjs';
import './globals.css';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="vi">
      <body>
        <CabinIdProvider>
          <Header />
          <main>{children}</main>
          <Footer />
        </CabinIdProvider>
      </body>
    </html>
  );
}

// components/Header.tsx
'use client';

import { useUser, SignInButton, SignOutButton } from '@cabin-id/nextjs';
import Link from 'next/link';

function Header() {
  const { isSignedIn, user, isLoaded } = useUser();

  return (
    <header className="bg-white shadow">
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <div className="flex justify-between h-16">
          <div className="flex items-center">
            <Link href="/" className="text-xl font-bold text-gray-900">
              Cửa hàng của tôi
            </Link>
          </div>
          
          <nav className="hidden md:flex items-center space-x-8">
            <Link href="/products" className="text-gray-500 hover:text-gray-900">
              Sản phẩm
            </Link>
            <Link href="/about" className="text-gray-500 hover:text-gray-900">
              Về chúng tôi
            </Link>
            <Link href="/contact" className="text-gray-500 hover:text-gray-900">
              Liên hệ
            </Link>
          </nav>

          <div className="flex items-center space-x-4">
            {!isLoaded ? (
              <div className="animate-pulse h-10 w-24 bg-gray-200 rounded"></div>
            ) : isSignedIn ? (
              <div className="flex items-center space-x-4">
                <Link 
                  href="/profile" 
                  className="text-gray-700 hover:text-gray-900"
                >
                  Xin chào, {user.firstName}
                </Link>
                <SignOutButton
                  className="rounded bg-red-600 px-4 py-2 text-white hover:bg-red-700"
                >
                  Đăng xuất
                </SignOutButton>
              </div>
            ) : (
              <SignInButton />
            )}
          </div>
        </div>
      </div>
    </header>
  );
}

🔧 Cấu hình nâng cao

Tùy chỉnh Domain

// middleware.ts
import { authMiddleware } from '@cabin-id/nextjs/server';

export default authMiddleware({
  // Domain tùy chỉnh
  domain: 'myapp.cabinid.dev',
  
  // Các route không cần xác thực
  publicRoutes: ['/', '/products', '/about', '/contact'],
  
  // Các route chỉ dành cho admin
  adminRoutes: ['/admin'],
  
  // Xử lý sau khi xác thực
  afterAuth(auth, req) {
    const { userId, isPublicRoute, isAdminRoute } = auth;
    
    // Chuyển hướng người dùng chưa đăng nhập
    if (!userId && !isPublicRoute) {
      return Response.redirect(new URL('/auth', req.url));
    }
    
    // Kiểm tra quyền admin (cần implement logic kiểm tra)
    if (isAdminRoute && !isAdmin(userId)) {
      return Response.redirect(new URL('/unauthorized', req.url));
    }
  },
});

function isAdmin(userId: string): boolean {
  // Logic kiểm tra quyền admin
  return false;
}

📚 API Reference

Components

  • SignInButton: Nút đăng nhập tự động chuyển hướng đến CabinID
  • SignOutButton: Nút đăng xuất với xử lý callback
  • CabinIdProvider: Provider chính để quản lý trạng thái xác thực

Hooks

  • useUser(): Hook để lấy thông tin người dùng hiện tại

Server Functions

  • getCurrentUser(): Lấy thông tin người dùng từ server
  • protect(): Bảo vệ API routes
  • authMiddleware(): Middleware xác thực cho Next.js

🐛 Xử lý lỗi thường gặp

Lỗi: "CabinIdProvider not found"

Đảm bảo bạn đã bọc ứng dụng với CabinIdProvider:

// ✅ Đúng
<CabinIdProvider>
  <YourApp />
</CabinIdProvider>

// ❌ Sai - thiếu provider
<YourApp />

Lỗi: Biến môi trường không được tìm thấy

Kiểm tra file .env.local và đảm bảo tên biến chính xác:

# ✅ Đúng
NEXT_PUBLIC_CABIN_ID_PUBLISH_KEY=cabin_pk_...
CABIN_ID_SECRET_KEY=cabin_sk_...

# ❌ Sai - thiếu NEXT_PUBLIC_
CABIN_ID_PUBLISH_KEY=cabin_pk_...

🔗 Liên kết hữu ích

📄 Giấy phép

MIT License - xem file LICENSE để biết thêm chi tiết.


🚀 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.