bknd-registration

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

User Registration Setup

用户注册设置

Configure and implement user self-registration in Bknd applications.
配置并在Bknd应用中实现用户自助注册。

Prerequisites

前提条件

  • Bknd project with auth enabled (
    bknd-setup-auth
    )
  • Password strategy configured
  • For SDK:
    bknd
    package installed
  • 已启用认证的Bknd项目(
    bknd-setup-auth
  • 已配置密码策略
  • 若使用SDK:已安装
    bknd

When to Use UI Mode

何时使用UI模式

  • Testing registration endpoint via admin panel
  • Viewing registered users
UI steps: Admin Panel > Auth > Test password/register endpoint
  • 通过管理面板测试注册端点
  • 查看已注册用户
UI操作步骤: 管理面板 > 认证 > 测试密码/注册端点

When to Use Code Mode

何时使用代码模式

  • Building registration forms in frontend
  • Configuring registration settings
  • Adding validation and error handling
  • 在前端构建注册表单
  • 配置注册设置
  • 添加验证和错误处理

Registration Configuration

注册配置

Enable/Disable Registration

启用/禁用注册

typescript
import { serve } from "bknd/adapter/bun";

serve({
  connection: { url: "file:data.db" },
  config: {
    auth: {
      enabled: true,
      allow_register: true,  // Enable self-registration (default: true)
      default_role_register: "user",  // Role assigned on registration
      strategies: {
        password: {
          type: "password",
          config: {
            hashing: "bcrypt",  // "plain" | "sha256" | "bcrypt"
            minLength: 8,       // Minimum password length
          },
        },
      },
      roles: {
        user: { implicit_allow: false },
      },
    },
  },
});
Config options:
OptionTypeDefaultDescription
allow_register
boolean
true
Enable self-registration
default_role_register
string-Role for new users
minLength
number8Minimum password length
typescript
import { serve } from "bknd/adapter/bun";

serve({
  connection: { url: "file:data.db" },
  config: {
    auth: {
      enabled: true,
      allow_register: true,  // 启用自助注册(默认值:true)
      default_role_register: "user",  // 注册时分配的角色
      strategies: {
        password: {
          type: "password",
          config: {
            hashing: "bcrypt",  // "plain" | "sha256" | "bcrypt"
            minLength: 8,       // 密码最小长度
          },
        },
      },
      roles: {
        user: { implicit_allow: false },
      },
    },
  },
});
配置选项:
选项类型默认值描述
allow_register
boolean
true
启用自助注册
default_role_register
string-新用户的角色
minLength
number8密码最小长度

SDK Registration

SDK注册

typescript
import { Api } from "bknd";

const api = new Api({
  host: "http://localhost:7654",
  storage: localStorage,  // Persist token
});

async function register(email: string, password: string) {
  const { ok, data, status } = await api.auth.register("password", {
    email,
    password,
  });

  if (ok) {
    // Token stored automatically - user is logged in
    return data.user;
  }

  if (status === 409) throw new Error("Email already registered");
  if (status === 400) throw new Error("Invalid email or password");
  throw new Error("Registration failed");
}
Response:
typescript
{
  ok: boolean;
  data?: {
    user: { id: number; email: string; role?: string };
    token: string;
  };
  status: number;
}
typescript
import { Api } from "bknd";

const api = new Api({
  host: "http://localhost:7654",
  storage: localStorage,  // 持久化存储令牌
});

async function register(email: string, password: string) {
  const { ok, data, status } = await api.auth.register("password", {
    email,
    password,
  });

  if (ok) {
    // 令牌会自动存储 - 用户已登录
    return data.user;
  }

  if (status === 409) throw new Error("邮箱已注册");
  if (status === 400) throw new Error("无效的邮箱或密码");
  throw new Error("注册失败");
}
响应:
typescript
{
  ok: boolean;
  data?: {
    user: { id: number; email: string; role?: string };
    token: string;
  };
  status: number;
}

REST API Registration

REST API注册

bash
curl -X POST http://localhost:7654/api/auth/password/register \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "password": "securepassword123"}'
Responses:
StatusMeaning
201Success - returns user + token
400Invalid email/password or too short
403Registration disabled
409Email already registered
bash
curl -X POST http://localhost:7654/api/auth/password/register \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "password": "securepassword123"}'
响应说明:
状态码含义
201成功 - 返回用户信息 + 令牌
400邮箱/密码无效或过短
403注册已禁用
409邮箱已注册

React Integration

React集成

Registration Form

注册表单

tsx
import { useState } from "react";
import { useApp } from "bknd/react";

function RegisterForm({ onSuccess }: { onSuccess?: () => void }) {
  const { api } = useApp();
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [confirmPassword, setConfirmPassword] = useState("");
  const [error, setError] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setError(null);

    if (password !== confirmPassword) {
      setError("Passwords do not match");
      return;
    }

    if (password.length < 8) {
      setError("Password must be at least 8 characters");
      return;
    }

    setLoading(true);
    const { ok, status } = await api.auth.register("password", {
      email,
      password,
    });
    setLoading(false);

    if (ok) {
      onSuccess?.();
    } else if (status === 409) {
      setError("Email already registered");
    } else {
      setError("Registration failed");
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      {error && <p className="error">{error}</p>}
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
        minLength={8}
        required
      />
      <input
        type="password"
        value={confirmPassword}
        onChange={(e) => setConfirmPassword(e.target.value)}
        placeholder="Confirm Password"
        required
      />
      <button disabled={loading}>
        {loading ? "Creating..." : "Create Account"}
      </button>
    </form>
  );
}
tsx
import { useState } from "react";
import { useApp } from "bknd/react";

function RegisterForm({ onSuccess }: { onSuccess?: () => void }) {
  const { api } = useApp();
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [confirmPassword, setConfirmPassword] = useState("");
  const [error, setError] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setError(null);

    if (password !== confirmPassword) {
      setError("密码不匹配");
      return;
    }

    if (password.length < 8) {
      setError("密码长度至少为8位");
      return;
    }

    setLoading(true);
    const { ok, status } = await api.auth.register("password", {
      email,
      password,
    });
    setLoading(false);

    if (ok) {
      onSuccess?.();
    } else if (status === 409) {
      setError("邮箱已注册");
    } else {
      setError("注册失败");
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      {error && <p className="error">{error}</p>}
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="邮箱"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="密码"
        minLength={8}
        required
      />
      <input
        type="password"
        value={confirmPassword}
        onChange={(e) => setConfirmPassword(e.target.value)}
        placeholder="确认密码"
        required
      />
      <button disabled={loading}>
        {loading ? "创建中..." : "创建账号"}
      </button>
    </form>
  );
}

Using useAuth Hook

使用useAuth钩子

tsx
import { useAuth } from "@bknd/react";

function RegisterPage() {
  const { user, isLoading, register } = useAuth();

  if (isLoading) return <div>Loading...</div>;
  if (user) return <Navigate to="/dashboard" />;

  async function handleRegister(email: string, password: string) {
    await register("password", { email, password });
  }

  return <RegisterForm onSuccess={() => navigate("/dashboard")} />;
}
tsx
import { useAuth } from "@bknd/react";

function RegisterPage() {
  const { user, isLoading, register } = useAuth();

  if (isLoading) return <div>加载中...</div>;
  if (user) return <Navigate to="/dashboard" />;

  async function handleRegister(email: string, password: string) {
    await register("password", { email, password });
  }

  return <RegisterForm onSuccess={() => navigate("/dashboard")} />;
}

Custom Fields After Registration

注册后添加自定义字段

Registration only accepts
email
and
password
. Add custom fields after:
typescript
// 1. Extend users entity
const schema = em({
  users: entity("users", {
    email: text().required().unique(),
    name: text(),
    avatar: text(),
  }),
});

// 2. Update user after registration
const { data } = await api.auth.register("password", { email, password });

await api.data.updateOne("users", data.user.id, {
  name: "John Doe",
  avatar: "https://...",
});
注册仅接受
email
password
。如需添加自定义字段,请在注册后操作:
typescript
// 1. 扩展users实体
const schema = em({
  users: entity("users", {
    email: text().required().unique(),
    name: text(),
    avatar: text(),
  }),
});

// 2. 注册后更新用户信息
const { data } = await api.auth.register("password", { email, password });

await api.data.updateOne("users", data.user.id, {
  name: "John Doe",
  avatar: "https://...",
});

Invite-Only Apps

仅邀请制应用

Disable public registration:
typescript
{
  auth: {
    allow_register: false,  // Disable self-registration
  },
}

// Admin creates users via seed or plugin
await app.module.auth.createUser({
  email: "invited@example.com",
  password: tempPassword,
  role: "user",
});
禁用公开注册:
typescript
{
  auth: {
    allow_register: false,  // 禁用自助注册
  },
}

// 管理员通过种子数据或插件创建用户
await app.module.auth.createUser({
  email: "invited@example.com",
  password: tempPassword,
  role: "user",
});

Common Pitfalls

常见问题

Registration Disabled

注册已禁用

Problem:
Registration not allowed
(403)
Fix:
{ auth: { allow_register: true } }
问题: 提示“Registration not allowed”(403状态码)
解决方法: 设置
{ auth: { allow_register: true } }

Role Not Found

角色不存在

Problem:
Role "user" not found
Fix: Define role before using:
typescript
{
  auth: {
    roles: { user: { implicit_allow: false } },
    default_role_register: "user",
  },
}
问题: 提示“Role "user" not found”
解决方法: 先定义角色再使用:
typescript
{
  auth: {
    roles: { user: { implicit_allow: false } },
    default_role_register: "user",
  },
}

User Already Exists

用户已存在

Problem: 409 error
Fix: Handle gracefully:
tsx
if (status === 409) {
  setError("Email already registered. Try logging in instead.");
}
问题: 409错误
解决方法: 友好处理该错误:
tsx
if (status === 409) {
  setError("邮箱已注册,请尝试登录。");
}

Token Not Stored

令牌未存储

Problem: User not logged in after registration
Fix: Provide storage:
typescript
const api = new Api({
  host: "http://localhost:7654",
  storage: localStorage,  // Required for persistence
});
问题: 注册后用户未自动登录
解决方法: 提供存储选项:
typescript
const api = new Api({
  host: "http://localhost:7654",
  storage: localStorage,  // 持久化存储所需
});

Custom Fields Ignored

自定义字段被忽略

Problem: Extra fields passed to registration not saved
Cause: Registration only accepts email/password
Fix: Update user after registration (see Custom Fields section)
问题: 注册时传入的额外字段未保存
原因: 注册仅接受邮箱和密码
解决方法: 注册后更新用户信息(参考自定义字段章节)

Verification

验证

bash
undefined
bash
undefined

1. Test registration

1. 测试注册

curl -X POST http://localhost:7654/api/auth/password/register
-H "Content-Type: application/json"
-d '{"email": "test@example.com", "password": "password123"}'
curl -X POST http://localhost:7654/api/auth/password/register
-H "Content-Type: application/json"
-d '{"email": "test@example.com", "password": "password123"}'

2. Verify token works

2. 验证令牌是否有效

curl http://localhost:7654/api/auth/me
-H "Authorization: Bearer <token>"
undefined
curl http://localhost:7654/api/auth/me
-H "Authorization: Bearer <token>"
undefined

DOs and DON'Ts

注意事项

DO:
  • Use bcrypt hashing in production
  • Validate password length client-side to match server config
  • Handle 409 error with login suggestion
  • Store token with
    storage: localStorage
  • Define roles before using
    default_role_register
DON'T:
  • Use
    hashing: "plain"
    in production
  • Expect custom fields in registration payload
  • Forget to handle registration errors
  • Disable registration without alternative user creation
建议:
  • 生产环境中使用bcrypt哈希算法
  • 客户端验证密码长度以匹配服务器配置
  • 处理409错误时提示用户尝试登录
  • 使用
    storage: localStorage
    存储令牌
  • 使用
    default_role_register
    前先定义角色
禁止:
  • 生产环境中使用
    hashing: "plain"
  • 期望注册请求体中包含自定义字段
  • 忽略注册错误处理
  • 禁用注册后不提供其他用户创建方式

Related Skills

相关技能

  • bknd-setup-auth - Configure authentication system
  • bknd-create-user - Programmatic user creation (admin/seed)
  • bknd-login-flow - Login/logout functionality
  • bknd-password-reset - Password reset flow
  • bknd-setup-auth - 配置认证系统
  • bknd-create-user - 程序化创建用户(管理员/种子数据)
  • bknd-login-flow - 登录/登出功能
  • bknd-password-reset - 密码重置流程