neon-auth-nextjs
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNeon Auth for Next.js
适用于Next.js的Neon Auth
Help developers set up @neondatabase/auth in Next.js App Router applications (auth only, no database).
帮助开发者在Next.js App Router应用中配置@neondatabase/auth(仅认证功能,无需数据库)。
When to Use
适用场景
Use this skill when:
- Setting up Neon Auth in Next.js (App Router)
- User mentions "next.js", "next", or "app router" with Neon Auth
- Auth-only setup (no database needed)
在以下场景中使用本指南:
- 在Next.js(App Router)中配置Neon Auth
- 用户提及"next.js"、"next"或"app router"并关联Neon Auth
- 仅需认证功能的场景(无需数据库)
Critical Rules
核心规则
- Server vs Client imports: Use correct import paths
- directive: Required for client components using hooks
'use client' - CSS Import: Choose ONE - either OR
/ui/css, never both/ui/tailwind - onSessionChange: Always call to update Server Components
router.refresh()
- 服务器端与客户端导入:使用正确的导入路径
- 指令:使用hooks的客户端组件必须添加该指令
'use client' - CSS导入:二选一 - 要么导入,要么导入
/ui/css,切勿同时导入两者/ui/tailwind - onSessionChange:必须调用以更新服务器组件
router.refresh()
Critical Imports
核心导入项
| Purpose | Import From |
|---|---|
Unified Server ( | |
| Client Auth | |
| UI Components | |
| View Paths (static params) | |
Note: Use from to get a unified instance that provides:
createNeonAuth()@neondatabase/auth/next/serverauth- - API route handler
.handler() - - Route protection middleware
.middleware() - All Better Auth server methods (,
.signIn,.signUp, etc.).getSession
| 用途 | 导入来源 |
|---|---|
统一服务器端( | |
| 客户端认证 | |
| UI组件 | |
| 视图路径(静态参数) | |
注意:从中使用获取统一的实例,该实例提供:
@neondatabase/auth/next/servercreateNeonAuth()auth- - API路由处理器
.handler() - - 路由保护中间件
.middleware() - 所有Better Auth服务器端方法(,
.signIn,.signUp等).getSession
Setup
配置步骤
1. Install
1. 安装
bash
npm install @neondatabase/authbash
npm install @neondatabase/auth2. Environment (.env.local
)
.env.local2. 环境配置(.env.local
)
.env.localNEON_AUTH_BASE_URL=https://your-auth.neon.tech
NEON_AUTH_COOKIE_SECRET=your-secret-at-least-32-characters-longImportant: Generate a secure secret (32+ characters) for production:
bash
openssl rand -base64 32NEON_AUTH_BASE_URL=https://your-auth.neon.tech
NEON_AUTH_COOKIE_SECRET=your-secret-at-least-32-characters-long重要提示:为生产环境生成一个安全的密钥(至少32个字符):
bash
openssl rand -base64 323. Server Setup (lib/auth/server.ts
)
lib/auth/server.ts3. 服务器端配置(lib/auth/server.ts
)
lib/auth/server.tsCreate a auth instance that provides handler, middleware, and server methods:
typescript
import { createNeonAuth } from '@neondatabase/auth/next/server';
export const auth = createNeonAuth({
baseUrl: process.env.NEON_AUTH_BASE_URL!,
cookies: {
secret: process.env.NEON_AUTH_COOKIE_SECRET!,
sessionDataTtl: 300, // Optional: session data cache TTL in seconds (default: 300 = 5 min)
domain: '.example.com', // Optional: for cross-subdomain cookies
},
});创建一个提供处理器、中间件和服务器端方法的auth实例:
typescript
import { createNeonAuth } from '@neondatabase/auth/next/server';
export const auth = createNeonAuth({
baseUrl: process.env.NEON_AUTH_BASE_URL!,
cookies: {
secret: process.env.NEON_AUTH_COOKIE_SECRET!,
sessionDataTtl: 300, // 可选:会话数据缓存过期时间(秒),默认值:300 = 5分钟
domain: '.example.com', // 可选:用于跨子域名共享Cookie
},
});4. API Route (app/api/auth/[...path]/route.ts
)
app/api/auth/[...path]/route.ts4. API路由(app/api/auth/[...path]/route.ts
)
app/api/auth/[...path]/route.tstypescript
import { auth } from '@/lib/auth/server';
export const { GET, POST } = auth.handler();typescript
import { auth } from '@/lib/auth/server';
export const { GET, POST } = auth.handler();5. Middleware (middleware.ts
)
middleware.ts5. 中间件(middleware.ts
)
middleware.tstypescript
import { auth } from '@/lib/auth/server';
export default auth.middleware({
loginUrl: '/auth/sign-in',
});
export const config = {
matcher: ['/dashboard/:path*', '/account/:path*'],
};typescript
import { auth } from '@/lib/auth/server';
export default auth.middleware({
loginUrl: '/auth/sign-in',
});
export const config = {
matcher: ['/dashboard/:path*', '/account/:path*'],
};6. Client (lib/auth/client.ts
)
lib/auth/client.ts6. 客户端配置(lib/auth/client.ts
)
lib/auth/client.tstypescript
'use client';
import { createAuthClient } from '@neondatabase/auth/next';
export const authClient = createAuthClient();typescript
'use client';
import { createAuthClient } from '@neondatabase/auth/next';
export const authClient = createAuthClient();7. Provider (app/providers.tsx
)
app/providers.tsx7. 提供者组件(app/providers.tsx
)
app/providers.tsxtypescript
'use client';
import { NeonAuthUIProvider } from '@neondatabase/auth/react/ui';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { authClient } from '@/lib/auth/client';
export function Providers({ children }: { children: React.ReactNode }) {
const router = useRouter();
return (
<NeonAuthUIProvider
authClient={authClient}
navigate={router.push}
replace={router.replace}
onSessionChange={() => router.refresh()}
redirectTo="/dashboard"
Link={({href, children}) => <Link to={href}>{children}</Link>}
>
{children}
</NeonAuthUIProvider>
);
}typescript
'use client';
import { NeonAuthUIProvider } from '@neondatabase/auth/react/ui';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { authClient } from '@/lib/auth/client';
export function Providers({ children }: { children: React.ReactNode }) {
const router = useRouter();
return (
<NeonAuthUIProvider
authClient={authClient}
navigate={router.push}
replace={router.replace}
onSessionChange={() => router.refresh()}
redirectTo="/dashboard"
Link={({href, children}) => <Link to={href}>{children}</Link>}
>
{children}
</NeonAuthUIProvider>
);
}8. Layout (app/layout.tsx
)
app/layout.tsx8. 布局文件(app/layout.tsx
)
app/layout.tsxtypescript
import { Providers } from './providers';
import '@neondatabase/auth/ui/css';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}typescript
import { Providers } from './providers';
import '@neondatabase/auth/ui/css';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}9. Auth Pages (app/auth/[path]/page.tsx
)
app/auth/[path]/page.tsx9. 认证页面(app/auth/[path]/page.tsx
)
app/auth/[path]/page.tsxtypescript
import { AuthView } from '@neondatabase/auth/react/ui';
import { authViewPaths } from '@neondatabase/auth/react/ui/server';
export function generateStaticParams() {
return Object.values(authViewPaths).map((path) => ({ path }));
}
export default async function AuthPage({ params }: { params: Promise<{ path: string }> }) {
const { path } = await params;
return <AuthView pathname={path} />;
}typescript
import { AuthView } from '@neondatabase/auth/react/ui';
import { authViewPaths } from '@neondatabase/auth/react/ui/server';
export function generateStaticParams() {
return Object.values(authViewPaths).map((path) => ({ path }));
}
export default async function AuthPage({ params }: { params: Promise<{ path: string }> }) {
const { path } = await params;
return <AuthView pathname={path} />;
}CSS & Styling
CSS与样式
Import Options
导入选项
Without Tailwind (pre-built CSS bundle ~47KB):
typescript
// app/layout.tsx
import '@neondatabase/auth/ui/css';With Tailwind CSS v4 ():
app/globals.csscss
@import 'tailwindcss';
@import '@neondatabase/auth/ui/tailwind';IMPORTANT: Never import both - causes duplicate styles.
不使用Tailwind(预构建CSS包约47KB):
typescript
// app/layout.tsx
import '@neondatabase/auth/ui/css';使用Tailwind CSS v4():
app/globals.csscss
@import 'tailwindcss';
@import '@neondatabase/auth/ui/tailwind';重要提示:切勿同时导入两者,否则会导致样式重复。
Dark Mode
深色模式
The provider includes . Control via prop:
next-themesdefaultThemetypescript
<NeonAuthUIProvider
defaultTheme="system" // 'light' | 'dark' | 'system'
// ...
>提供者组件集成了。可通过属性控制:
next-themesdefaultThemetypescript
<NeonAuthUIProvider
defaultTheme="system" // 可选值:'light' | 'dark' | 'system'
// ...
>Custom Theming
自定义主题
Override CSS variables in :
globals.csscss
:root {
--primary: hsl(221.2 83.2% 53.3%);
--primary-foreground: hsl(210 40% 98%);
--background: hsl(0 0% 100%);
--foreground: hsl(222.2 84% 4.9%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(222.2 84% 4.9%);
--border: hsl(214.3 31.8% 91.4%);
--input: hsl(214.3 31.8% 91.4%);
--ring: hsl(221.2 83.2% 53.3%);
--radius: 0.5rem;
}
.dark {
--background: hsl(222.2 84% 4.9%);
--foreground: hsl(210 40% 98%);
/* ... dark mode overrides */
}在中覆盖CSS变量:
globals.csscss
:root {
--primary: hsl(221.2 83.2% 53.3%);
--primary-foreground: hsl(210 40% 98%);
--background: hsl(0 0% 100%);
--foreground: hsl(222.2 84% 4.9%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(222.2 84% 4.9%);
--border: hsl(214.3 31.8% 91.4%);
--input: hsl(214.3 31.8% 91.4%);
--ring: hsl(221.2 83.2% 53.3%);
--radius: 0.5rem;
}
.dark {
--background: hsl(222.2 84% 4.9%);
--foreground: hsl(210 40% 98%);
/* ... 深色模式覆盖样式 */
}NeonAuthUIProvider Props
NeonAuthUIProvider属性
Full configuration options:
typescript
<NeonAuthUIProvider
// Required
authClient={authClient}
// Navigation (Next.js specific)
navigate={router.push} // router.push for navigation
replace={router.replace} // router.replace for redirects
onSessionChange={() => router.refresh()} // Refresh Server Components!
redirectTo="/dashboard" // Where to redirect after auth
Link={({href, children}) => <Link to={href}>{children}</Link>} // Next.js Link component
// Social/OAuth Providers
social={{
providers: ['google'],
}}
// Feature Flags
emailOTP={true} // Enable email OTP sign-in
emailVerification={true} // Require email verification
magicLink={false} // Magic link (disabled by default)
multiSession={false} // Multiple sessions (disabled)
// Credentials Configuration
credentials={{
forgotPassword: true, // Show forgot password link
}}
// Sign Up Fields
signUp={{
fields: ['name'], // Additional fields: 'name', 'username', etc.
}}
// Account Settings Fields
account={{
fields: ['image', 'name', 'company', 'age', 'newsletter'],
}}
// Organization Features
organization={{}} // Enable org features
// Dark Mode
defaultTheme="system" // 'light' | 'dark' | 'system'
// Custom Labels
localization={{
SIGN_IN: 'Welcome Back',
SIGN_UP: 'Create Account',
FORGOT_PASSWORD: 'Forgot Password?',
OR_CONTINUE_WITH: 'or continue with',
}}
>
{children}
</NeonAuthUIProvider>完整配置选项:
typescript
<NeonAuthUIProvider
// 必填项
authClient={authClient}
// 导航(Next.js专属)
navigate={router.push} // 使用router.push进行导航
replace={router.replace} // 使用router.replace进行重定向
onSessionChange={() => router.refresh()} // 刷新服务器组件!
redirectTo="/dashboard" // 认证完成后重定向的目标路径
Link={({href, children}) => <Link to={href}>{children}</Link>} // Next.js Link组件
// 社交/OAuth提供者
social={{
providers: ['google'],
}}
// 功能开关
emailOTP={true} // 启用邮箱OTP登录
emailVerification={true} // 要求邮箱验证
magicLink={false} // 魔法链接(默认禁用)
multiSession={false} // 多会话支持(默认禁用)
// 凭证配置
credentials={{
forgotPassword: true, // 显示忘记密码链接
}}
// 注册字段
signUp={{
fields: ['name'], // 额外字段:'name', 'username'等
}}
// 账户设置字段
account={{
fields: ['image', 'name', 'company', 'age', 'newsletter'],
}}
// 组织功能
organization={{}} // 启用组织功能
// 深色模式
defaultTheme="system" // 可选值:'light' | 'dark' | 'system'
// 自定义标签文本
localization={{
SIGN_IN: '欢迎回来',
SIGN_UP: '创建账户',
FORGOT_PASSWORD: '忘记密码?',
OR_CONTINUE_WITH: '或通过以下方式登录',
}}
>
{children}
</NeonAuthUIProvider>Server Components (RSC)
服务器组件(RSC)
Get Session in Server Component
在服务器组件中获取会话
typescript
// NO 'use client' - this is a Server Component
import { auth } from '@/lib/auth/server';
// Server components using `auth` methods must be rendered dynamically
export const dynamic = 'force-dynamic'
export async function Profile() {
const { data: session } = await auth.getSession();
if (!session?.user) return <div>Not signed in</div>;
return (
<div>
<p>Hello, {session.user.name}</p>
<p>Email: {session.user.email}</p>
</div>
);
}typescript
// 无需添加'use client' - 这是一个服务器组件
import { auth } from '@/lib/auth/server';
// 使用`auth`方法的服务器组件必须动态渲染
export const dynamic = 'force-dynamic'
export async function Profile() {
const { data: session } = await auth.getSession();
if (!session?.user) return <div>未登录</div>;
return (
<div>
<p>你好,{session.user.name}</p>
<p>邮箱:{session.user.email}</p>
</div>
);
}Route Handler with Auth
带认证的路由处理器
typescript
// app/api/user/route.ts
import { auth } from '@/lib/auth/server';
import { NextResponse } from 'next/server';
export async function GET() {
const { data: session } = await auth.getSession();
if (!session?.user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
return NextResponse.json({ user: session.user });
}typescript
// app/api/user/route.ts
import { auth } from '@/lib/auth/server';
import { NextResponse } from 'next/server';
export async function GET() {
const { data: session } = await auth.getSession();
if (!session?.user) {
return NextResponse.json({ error: '未授权' }, { status: 401 });
}
return NextResponse.json({ user: session.user });
}Server Actions
服务器操作
Server actions use the same instance from :
authlib/auth/server.ts服务器操作使用中的同一个实例:
lib/auth/server.tsauthSign In Action
登录操作
typescript
// app/actions/auth.ts
'use server';
import { auth } from '@/lib/auth/server';
import { redirect } from 'next/navigation';
export async function signIn(formData: FormData) {
const { error } = await auth.signIn.email({
email: formData.get('email') as string,
password: formData.get('password') as string,
});
if (error) {
return { error: error.message };
}
redirect('/dashboard');
}
export async function signUp(formData: FormData) {
const { error } = await auth.signUp.email({
email: formData.get('email') as string,
password: formData.get('password') as string,
name: formData.get('name') as string,
});
if (error) {
return { error: error.message };
}
redirect('/dashboard');
}
export async function signOut() {
await auth.signOut();
redirect('/');
}typescript
// app/actions/auth.ts
'use server';
import { auth } from '@/lib/auth/server';
import { redirect } from 'next/navigation';
export async function signIn(formData: FormData) {
const { error } = await auth.signIn.email({
email: formData.get('email') as string,
password: formData.get('password') as string,
});
if (error) {
return { error: error.message };
}
redirect('/dashboard');
}
export async function signUp(formData: FormData) {
const { error } = await auth.signUp.email({
email: formData.get('email') as string,
password: formData.get('password') as string,
name: formData.get('name') as string,
});
if (error) {
return { error: error.message };
}
redirect('/dashboard');
}
export async function signOut() {
await auth.signOut();
redirect('/');
}Available Server Methods
可用的服务器端方法
The instance from provides all Better Auth server methods:
authcreateNeonAuth()typescript
// Authentication
auth.signIn.email({ email, password })
auth.signUp.email({ email, password, name })
auth.signOut()
auth.getSession()
// User Management
auth.updateUser({ name, image })
// Organizations
auth.organization.create({ name, slug })
auth.organization.list()
// Admin (if enabled)
auth.admin.listUsers()
auth.admin.banUser({ userId })createNeonAuth()authtypescript
// 认证相关
auth.signIn.email({ email, password })
auth.signUp.email({ email, password, name })
auth.signOut()
auth.getSession()
// 用户管理
auth.updateUser({ name, image })
// 组织相关
auth.organization.create({ name, slug })
auth.organization.list()
// 管理员功能(若启用)
auth.admin.listUsers()
auth.admin.banUser({ userId })Client Components
客户端组件
Session Hook
会话Hook
typescript
'use client';
import { authClient } from '@/lib/auth/client';
export function Dashboard() {
const { data: session, isPending, error } = authClient.useSession();
if (isPending) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!session) return <div>Not signed in</div>;
return <div>Hello, {session.user.name}</div>;
}typescript
'use client';
import { authClient } from '@/lib/auth/client';
export function Dashboard() {
const { data: session, isPending, error } = authClient.useSession();
if (isPending) return <div>加载中...</div>;
if (error) return <div>错误:{error.message}</div>;
if (!session) return <div>未登录</div>;
return <div>你好,{session.user.name}</div>;
}Client-Side Auth Methods
客户端认证方法
typescript
'use client';
import { authClient } from '@/lib/auth/client';
// Sign in
await authClient.signIn.email({ email, password });
// Sign up
await authClient.signUp.email({ email, password, name });
// OAuth
await authClient.signIn.social({
provider: 'google',
callbackURL: '/dashboard',
});
// Sign out
await authClient.signOut();
// Get session
const session = await authClient.getSession();typescript
'use client';
import { authClient } from '@/lib/auth/client';
// 登录
await authClient.signIn.email({ email, password });
// 注册
await authClient.signUp.email({ email, password, name });
// OAuth登录
await authClient.signIn.social({
provider: 'google',
callbackURL: '/dashboard',
});
// 登出
await authClient.signOut();
// 获取会话
const session = await authClient.getSession();UI Components
UI组件
AuthView - Main Auth Interface
AuthView - 主认证界面
typescript
import { AuthView } from '@neondatabase/auth/react/ui';
// Handles: sign-in, sign-up, forgot-password, reset-password, callback, sign-out
<AuthView pathname={path} />typescript
import { AuthView } from '@neondatabase/auth/react/ui';
// 处理:登录、注册、忘记密码、重置密码、回调、登出
<AuthView pathname={path} />Conditional Rendering
条件渲染
typescript
import {
SignedIn,
SignedOut,
AuthLoading,
RedirectToSignIn,
} from '@neondatabase/auth/react/ui';
function MyPage() {
return (
<>
<AuthLoading>
<LoadingSpinner />
</AuthLoading>
<SignedIn>
<Dashboard />
</SignedIn>
<SignedOut>
<LandingPage />
</SignedOut>
{/* Auto-redirect if not signed in */}
<RedirectToSignIn />
</>
);
}typescript
import {
SignedIn,
SignedOut,
AuthLoading,
RedirectToSignIn,
} from '@neondatabase/auth/react/ui';
function MyPage() {
return (
<>
<AuthLoading>
<LoadingSpinner />
</AuthLoading>
<SignedIn>
<Dashboard />
</SignedIn>
<SignedOut>
<LandingPage />
</SignedOut>
{/* 未登录时自动重定向 */}
<RedirectToSignIn />
</>
);
}UserButton
用户按钮
typescript
import { UserButton } from '@neondatabase/auth/react/ui';
function Header() {
return (
<header>
<nav>...</nav>
<UserButton />
</header>
);
}typescript
import { UserButton } from '@neondatabase/auth/react/ui';
function Header() {
return (
<header>
<nav>...</nav>
<UserButton />
</header>
);
}Account Management
账户管理组件
typescript
import {
AccountSettingsCards,
SecuritySettingsCards,
SessionsCard,
ChangePasswordCard,
ChangeEmailCard,
DeleteAccountCard,
ProvidersCard,
} from '@neondatabase/auth/react/ui';typescript
import {
AccountSettingsCards,
SecuritySettingsCards,
SessionsCard,
ChangePasswordCard,
ChangeEmailCard,
DeleteAccountCard,
ProvidersCard,
} from '@neondatabase/auth/react/ui';Organization Components
组织组件
typescript
import {
OrganizationSwitcher,
OrganizationSettingsCards,
OrganizationMembersCard,
AcceptInvitationCard,
} from '@neondatabase/auth/react/ui';typescript
import {
OrganizationSwitcher,
OrganizationSettingsCards,
OrganizationMembersCard,
AcceptInvitationCard,
} from '@neondatabase/auth/react/ui';Social/OAuth Providers
社交/OAuth提供者
Configuration
配置
typescript
<NeonAuthUIProvider
social={{
providers: ['google'],
}}
>typescript
<NeonAuthUIProvider
social={{
providers: ['google'],
}}
>Programmatic OAuth
程序化OAuth
typescript
// Client-side
await authClient.signIn.social({
provider: 'google',
callbackURL: '/dashboard',
});typescript
// 客户端
await authClient.signIn.social({
provider: 'google',
callbackURL: '/dashboard',
});Supported Providers
支持的提供者
googlegithubtwitterdiscordapplemicrosoftfacebooklinkedinspotifytwitchgitlabbitbucketgooglegithubtwitterdiscordapplemicrosoftfacebooklinkedinspotifytwitchgitlabbitbucketMiddleware Configuration
中间件配置
Basic Protected Routes
基础保护路由
typescript
import { auth } from '@/lib/auth/server';
export default auth.middleware({
loginUrl: '/auth/sign-in',
});
export const config = {
matcher: ['/dashboard/:path*', '/account/:path*', '/settings/:path*'],
};typescript
import { auth } from '@/lib/auth/server';
export default auth.middleware({
loginUrl: '/auth/sign-in',
});
export const config = {
matcher: ['/dashboard/:path*', '/account/:path*', '/settings/:path*'],
};Custom Logic
自定义逻辑
typescript
import { auth } from '@/lib/auth/server';
import { NextResponse } from 'next/server';
export default auth.middleware({
loginUrl: '/auth/sign-in',
});typescript
import { auth } from '@/lib/auth/server';
import { NextResponse } from 'next/server';
export default auth.middleware({
loginUrl: '/auth/sign-in',
});Account Pages Setup
账户页面配置
Account Layout (app/account/[path]/page.tsx
)
app/account/[path]/page.tsx账户布局(app/account/[path]/page.tsx
)
app/account/[path]/page.tsxtypescript
import {
SignedIn,
RedirectToSignIn,
AccountSettingsCards,
SecuritySettingsCards,
SessionsCard,
ChangePasswordCard,
} from '@neondatabase/auth/react/ui';
export default async function AccountPage({ params }: { params: Promise<{ path: string }> }) {
const { path = 'settings' } = await params;
return (
<>
<RedirectToSignIn />
<SignedIn>
{path === 'settings' && <AccountSettingsCards />}
{path === 'security' && (
<>
<ChangePasswordCard />
<SecuritySettingsCards />
</>
)}
{path === 'sessions' && <SessionsCard />}
</SignedIn>
</>
);
}typescript
import {
SignedIn,
RedirectToSignIn,
AccountSettingsCards,
SecuritySettingsCards,
SessionsCard,
ChangePasswordCard,
} from '@neondatabase/auth/react/ui';
export default async function AccountPage({ params }: { params: Promise<{ path: string }> }) {
const { path = 'settings' } = await params;
return (
<>
<RedirectToSignIn />
<SignedIn>
{path === 'settings' && <AccountSettingsCards />}
{path === 'security' && (
<>
<ChangePasswordCard />
<SecuritySettingsCards />
</>
)}
{path === 'sessions' && <SessionsCard />}
</SignedIn>
</>
);
}Advanced Features
高级功能
Anonymous Access
匿名访问
Enable RLS-based data access for unauthenticated users:
typescript
// lib/auth/client.ts
'use client';
import { createAuthClient } from '@neondatabase/auth/next';
export const authClient = createAuthClient({
allowAnonymous: true,
});为未认证用户启用基于RLS的数据访问:
typescript
// lib/auth/client.ts
'use client';
import { createAuthClient } from '@neondatabase/auth/next';
export const authClient = createAuthClient({
allowAnonymous: true,
});Get JWT Token
获取JWT令牌
typescript
const token = await authClient.getJWTToken();
// Use in API calls
const response = await fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` },
});typescript
const token = await authClient.getJWTToken();
// 在API调用中使用
const response = await fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` },
});Cross-Tab Sync
跨标签页同步
Automatic via BroadcastChannel. Sign out in one tab signs out all tabs.
通过BroadcastChannel自动实现。在一个标签页登出会同步所有标签页的登出状态。
Session Refresh in Server Components
服务器组件中的会话刷新
The callback is crucial for Next.js:
onSessionChangetypescript
<NeonAuthUIProvider
onSessionChange={() => router.refresh()} // Refreshes Server Components!
// ...
>Without this, Server Components won't update after sign-in/sign-out.
onSessionChangetypescript
<NeonAuthUIProvider
onSessionChange={() => router.refresh()} // 刷新服务器组件!
// ...
>如果没有该回调,服务器组件在登录/登出后不会更新。
Error Handling
错误处理
Server Actions
服务器操作
typescript
'use server';
export async function signIn(formData: FormData) {
const { error } = await authServer.signIn.email({
email: formData.get('email') as string,
password: formData.get('password') as string,
});
if (error) {
// Return error to client
return { error: error.message };
}
redirect('/dashboard');
}typescript
'use server';
export async function signIn(formData: FormData) {
const { error } = await authServer.signIn.email({
email: formData.get('email') as string,
password: formData.get('password') as string,
});
if (error) {
// 将错误返回给客户端
return { error: error.message };
}
redirect('/dashboard');
}Client Components
客户端组件
typescript
'use client';
const { error } = await authClient.signIn.email({ email, password });
if (error) {
toast.error(error.message);
}typescript
'use client';
const { error } = await authClient.signIn.email({ email, password });
if (error) {
toast.error(error.message);
}Common Errors
常见错误
| Error | Cause |
|---|---|
| Wrong email/password |
| Email already registered |
| Verification required |
| Expired or invalid session |
| 错误信息 | 原因 |
|---|---|
| 邮箱或密码错误 |
| 该邮箱已注册 |
| 需要验证邮箱 |
| 会话已过期或无效 |
FAQ / Troubleshooting
FAQ / 故障排除
Server Components not updating after sign-in?
登录后服务器组件未更新?
Make sure you have in your provider:
onSessionChange={() => router.refresh()}typescript
<NeonAuthUIProvider
onSessionChange={() => router.refresh()}
// ...
>确保在提供者组件中添加了:
onSessionChange={() => router.refresh()}typescript
<NeonAuthUIProvider
onSessionChange={() => router.refresh()}
// ...
>Anonymous access not working?
匿名访问无法工作?
Grant permissions to the role in your database:
anonymoussql
GRANT SELECT ON public.posts TO anonymous;
GRANT SELECT ON public.products TO anonymous;And configure RLS policies:
sql
CREATE POLICY "Anyone can read published posts"
ON public.posts FOR SELECT
USING (published = true);在数据库中为角色授予权限:
anonymoussql
GRANT SELECT ON public.posts TO anonymous;
GRANT SELECT ON public.products TO anonymous;并配置RLS策略:
sql
CREATE POLICY "任何人都可以查看已发布的文章"
ON public.posts FOR SELECT
USING (published = true);Middleware not protecting routes?
中间件未保护路由?
Check your configuration:
matchertypescript
export const config = {
matcher: [
'/dashboard/:path*',
'/account/:path*',
// Add your protected routes here
],
};检查配置:
matchertypescript
export const config = {
matcher: [
'/dashboard/:path*',
'/account/:path*',
// 在此添加需要保护的路由
],
};OAuth callback errors?
OAuth回调出错?
Ensure your API route is set up correctly at :
app/api/auth/[...path]/route.tstypescript
import { auth } from '@/lib/auth/server';
export const { GET, POST } = auth.handler();确保API路由已在中正确配置:
app/api/auth/[...path]/route.tstypescript
import { auth } from '@/lib/auth/server';
export const { GET, POST } = auth.handler();Session not persisting?
会话未持久化?
- Check cookies are enabled
- Verify is correct in
NEON_AUTH_BASE_URL.env.local - Verify is set and at least 32 characters
NEON_AUTH_COOKIE_SECRET - Make sure you're not in incognito with cookies blocked
- 检查Cookie是否已启用
- 验证中的
.env.local是否正确NEON_AUTH_BASE_URL - 验证已设置且至少32个字符
NEON_AUTH_COOKIE_SECRET - 确保未在阻止Cookie的隐身模式下测试
Session data cache not working?
会话数据缓存不工作?
- Verify is at least 32 characters long
NEON_AUTH_COOKIE_SECRET - Check is passed to
cookies.secretcreateNeonAuth() - Optionally configure (default: 300 seconds)
cookies.sessionDataTtl
- 验证至少32个字符
NEON_AUTH_COOKIE_SECRET - 检查是否已传递给
cookies.secretcreateNeonAuth() - 可选择性配置(默认值:300秒)
cookies.sessionDataTtl
Performance Notes
性能说明
- Session data caching: JWT-signed cookie with configurable TTL (default: 5 minutes)
session_data- Configure via in seconds
cookies.sessionDataTtl - Enables sub-millisecond session lookups (<1ms)
- Automatic fallback to upstream on cache miss
/get-session
- Configure via
- Request deduplication: Concurrent calls share single network request (10x faster cold starts)
- Server Components: Use for zero-JS session access
auth.getSession() - Cross-tab sync: <50ms via BroadcastChannel
- Cookie domain: Optional for cross-subdomain cookie sharing
cookies.domain
- 会话数据缓存:使用JWT签名的Cookie,可配置过期时间(默认:5分钟)
session_data- 通过(秒)配置
cookies.sessionDataTtl - 实现亚毫秒级的会话查询(<1ms)
- 缓存失效时自动回退到上游接口
/get-session
- 通过
- 请求去重:并发请求共享单个网络请求(冷启动速度提升10倍)
- 服务器组件:使用实现零JS会话访问
auth.getSession() - 跨标签页同步:通过BroadcastChannel实现,延迟<50ms
- Cookie域名:可选的用于跨子域名共享Cookie
cookies.domain