.agents/skills/bknd-create-user/SKILL.md
Use when creating a new user account in Bknd programmatically. Covers auth.createUser() in seed functions, registration via SDK/REST API, creating users via data API, admin panel user creation, and role assignment.
npx skillsauth add cameronapak/freedom-stack-v3 bknd-create-userInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Create new user accounts in Bknd via seed functions, SDK, REST API, or admin panel.
auth.enabled: true)UI steps: Admin Panel > Auth > Users > Click "+" > Fill email/password > Select role > Save
Create users on first app startup via seed function:
import { serve } from "bknd/adapter/bun";
import { em, entity, text } from "bknd";
const schema = em({
posts: entity("posts", { title: text().required() }),
});
serve({
connection: { url: "file:data.db" },
config: {
data: schema.toJSON(),
auth: {
enabled: true,
jwt: { secret: process.env.JWT_SECRET || "dev-secret" },
roles: {
admin: { implicit_allow: true },
user: { implicit_allow: false },
},
},
},
options: {
seed: async (ctx) => {
// Create admin user on first run
await ctx.app.module.auth.createUser({
email: "[email protected]",
password: "securepassword123",
role: "admin",
});
console.log("Admin user created");
},
},
});
Seed function notes:
ctx.app.module.authCreate users programmatically in server code (plugins, flows, custom endpoints):
import { getApi } from "bknd";
// In a plugin, flow, or custom endpoint handler
async function createAdminUser(app) {
const user = await app.module.auth.createUser({
email: "[email protected]",
password: "securepassword123",
role: "admin",
});
console.log("Created user:", user.id, user.email);
return user;
}
// With additional fields (if users entity has custom fields)
async function createUserWithProfile(app) {
const user = await app.module.auth.createUser({
email: "[email protected]",
password: "password123",
role: "user",
name: "John Doe", // Custom field
avatar: "https://...", // Custom field
});
return user;
}
createUser() signature:
type CreateUserPayload = {
email: string; // Required: user email
password: string; // Required: plain text (will be hashed)
role?: string; // Optional: must exist in auth.roles
[key: string]: any; // Additional fields for users entity
};
// Returns the created user record
async createUser(payload: CreateUserPayload): Promise<User>
For user self-registration via your frontend:
import { Api } from "bknd";
const api = new Api({
host: "http://localhost:7654",
storage: localStorage, // For token persistence
});
// Register new user
const { ok, data, error } = await api.auth.register("password", {
email: "[email protected]",
password: "securepassword123",
});
if (ok) {
console.log("Registered:", data.user);
console.log("Token:", data.token);
// User is now logged in, token stored in localStorage
} else {
console.error("Registration failed:", error);
}
Registration notes:
auth.allow_register: true (default)auth.default_role_register role automatically# Register via REST API
curl -X POST http://localhost:7654/api/auth/password/register \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "securepassword123"}'
Response:
{
"user": {
"id": 1,
"email": "[email protected]",
"role": "user"
},
"token": "eyJhbGciOiJIUzI1NiIs..."
}
Admins can create users directly via data API (requires auth + admin role):
// As authenticated admin
const { ok, data } = await api.data.createOne("users", {
email: "[email protected]",
strategy: "password",
strategy_value: "HASHED_PASSWORD", // Must be pre-hashed!
role: "user",
});
Warning: Data API requires pre-hashed password. Use createUser() or registration instead for proper password handling.
import { useApp } from "bknd/react";
import { useState } from "react";
function RegisterForm() {
const { api } = useApp();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setLoading(true);
setError(null);
const { ok, data, error: apiError } = await api.auth.register("password", {
email,
password,
});
setLoading(false);
if (ok) {
console.log("Registered:", data.user);
// Redirect to dashboard or show success
} else {
setError(apiError?.message || "Registration failed");
}
}
return (
<form onSubmit={handleSubmit}>
<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
/>
<button type="submit" disabled={loading}>
{loading ? "Creating account..." : "Register"}
</button>
{error && <p className="error">{error}</p>}
</form>
);
}
import { useAuth } from "@bknd/react";
function AuthStatus() {
const { user, isLoading, register, logout } = useAuth();
if (isLoading) return <div>Loading...</div>;
if (!user) {
return (
<button onClick={() => register("password", {
email: "[email protected]",
password: "password123"
})}>
Create Account
</button>
);
}
return (
<div>
<p>Welcome, {user.email}</p>
<button onClick={logout}>Logout</button>
</div>
);
}
{
auth: {
enabled: true,
allow_register: true, // Set to false to disable self-registration
default_role_register: "user", // Role assigned on registration
},
}
{
auth: {
strategies: {
password: {
type: "password",
enabled: true,
config: {
hashing: "bcrypt", // "plain" | "sha256" | "bcrypt"
rounds: 4, // bcrypt rounds (1-10)
minLength: 8, // Minimum password length
},
},
},
},
}
{
auth: {
roles: {
admin: {
implicit_allow: true, // Can do everything
},
editor: {
implicit_allow: false,
permissions: [
{ permission: "data.posts.read", effect: "allow" },
{ permission: "data.posts.create", effect: "allow" },
{ permission: "data.posts.update", effect: "allow" },
],
},
user: {
implicit_allow: false,
permissions: [
{ permission: "data.posts.read", effect: "allow" },
],
},
},
default_role_register: "user", // New registrations get this role
},
}
Add custom fields to users:
import { em, entity, text, date } from "bknd";
const schema = em({
users: entity("users", {
email: text().required().unique(),
name: text(),
avatar: text(),
bio: text(),
created_at: date({ default_value: "now" }),
}),
});
// In config
{
config: {
data: schema.toJSON(),
auth: { enabled: true, /* ... */ },
},
}
Note: strategy and strategy_value fields are managed by auth system - don't modify directly.
Problem: Registration not allowed error
Fix: Enable registration:
{ auth: { allow_register: true } }
Problem: Role "admin" not found error
Fix: Define role in config before assigning:
{
auth: {
roles: {
admin: { implicit_allow: true },
},
},
}
Problem: User already exists or UNIQUE constraint failed
Fix: Check before creating or handle error:
// SDK registration handles this automatically
const { ok, error } = await api.auth.register("password", { email, password });
if (!ok && error?.message?.includes("exists")) {
console.log("Email already registered");
}
// Server-side: check first
const { data: exists } = await api.data.exists("users", {
email: { $eq: email },
});
if (!exists.exists) {
await app.module.auth.createUser({ email, password });
}
Problem: Cannot sign JWT without secret or security warnings
Fix: Set strong JWT secret:
{
auth: {
jwt: {
secret: process.env.JWT_SECRET, // Use env var, 256-bit minimum
},
},
}
Problem: User can't login after creating via data API
Cause: Data API doesn't hash passwords automatically
Fix: Use createUser() or registration instead:
// Wrong - password not hashed
await api.data.createOne("users", { email, password: "plain" });
// Correct - use auth module
await app.module.auth.createUser({ email, password: "plain" });
Problem: Custom fields not included after registration
Cause: Registration only accepts email/password
Fix: Update user after registration:
const { data } = await api.auth.register("password", { email, password });
// Update with additional fields
await api.data.updateOne("users", data.user.id, {
name: "John Doe",
avatar: "https://...",
});
After creating a user, verify:
// SDK - check current user after registration
const { data } = await api.auth.me();
console.log("Current user:", data?.user);
// Server-side - read back
const { data: user } = await api.data.readOneBy("users", {
where: { email: { $eq: "[email protected]" } },
});
console.log("Created user:", user);
Or via admin panel: Admin Panel > Auth > Users > Find new user in list.
DO:
createUser() or registration for proper password hashingDON'T:
strategy or strategy_value fields directlydevelopment
Use btca (Better Context App) to efficiently query and learn from the bknd backend framework. Use when working with bknd for (1) Understanding data module and schema definitions, (2) Implementing authentication and authorization, (3) Setting up media file handling, (4) Configuring adapters (Node, Cloudflare, etc.), (5) Learning from bknd source code and examples, (6) Debugging bknd-specific issues
development
Use when configuring webhook integrations in Bknd. Covers receiving incoming webhooks via HTTP triggers, sending outgoing webhooks with FetchTask, event-triggered webhooks on data changes, signature verification, retry patterns, and async processing.
development
Use when encountering Bknd errors, getting error messages, something not working, or needing quick fixes. Covers error code reference, quick solutions, and common mistake patterns.
tools
Use when writing tests for Bknd applications, setting up test infrastructure, creating unit/integration tests, or testing API endpoints. Covers in-memory database setup, test helpers, mocking, and test patterns.