
bknd-setup-auth
Use when initializing or configuring the Bknd authentication system. Covers enabling auth, configuring password strategy, setting up JWT and cookie options, defining roles, and production security settings.
Use when initializing or configuring the Bknd authentication system. Covers enabling auth, configuring password strategy, setting up JWT and cookie options, defining roles, and production security settings.
Setup Authentication
Initialize and configure the Bknd authentication system with strategies, JWT, cookies, and roles.
Prerequisites
- Bknd project initialized
- Code-first configuration (auth config is code-only)
- For OAuth: provider credentials (client ID, client secret)
When to Use UI Mode
- Viewing current auth configuration
- Toggling strategies on/off
- Testing auth endpoints via admin panel
UI steps: Admin Panel > Auth > Settings
Note: Full auth configuration requires code mode. UI only shows/toggles existing settings.
When to Use Code Mode
- Initial authentication setup
- Configuring JWT secrets and expiry
- Setting up password hashing
- Defining roles and permissions
- Production security hardening
Code Approach
Step 1: Enable Auth with Minimal Config
Start with basic password authentication:
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,
},
},
});
This enables:
- Password strategy (default)
- Auto-created
usersentity - JWT-based sessions
/api/auth/*endpoints
Step 2: Configure JWT Settings
JWT tokens authenticate API requests. Configure for security:
{
auth: {
enabled: true,
jwt: {
secret: process.env.JWT_SECRET, // Required in production
alg: "HS256", // Algorithm: HS256, HS384, HS512
expires: 604800, // Expiry in seconds (7 days)
issuer: "my-app", // Optional issuer claim
fields: ["id", "email", "role"], // Fields included in token
},
},
}
JWT options:
| Option | Type | Default | Description |
|---|---|---|---|
secret |
string | "" |
Signing secret (256-bit minimum for production) |
alg |
string | "HS256" |
Algorithm: HS256, HS384, HS512 |
expires |
number | - | Token expiry in seconds |
issuer |
string | - | Token issuer claim (iss) |
fields |
string[] | ["id", "email", "role"] |
User fields in payload |
Step 3: Configure Cookie Settings
Auth cookies store JWT tokens for browser sessions:
{
auth: {
enabled: true,
jwt: { secret: process.env.JWT_SECRET },
cookie: {
secure: true, // HTTPS only (set false for local dev)
httpOnly: true, // Block JavaScript access
sameSite: "lax", // CSRF protection: "strict" | "lax" | "none"
expires: 604800, // Cookie expiry in seconds (7 days)
path: "/", // Cookie path scope
renew: true, // Auto-extend on requests
pathSuccess: "/", // Redirect after login
pathLoggedOut: "/", // Redirect after logout
},
},
}
Cookie options:
| Option | Type | Default | Description |
|---|---|---|---|
secure |
boolean | true |
HTTPS-only flag |
httpOnly |
boolean | true |
Block JS access |
sameSite |
string | "lax" |
CSRF protection |
expires |
number | 604800 |
Expiry in seconds |
renew |
boolean | true |
Auto-extend expiry |
pathSuccess |
string | "/" |
Post-login redirect |
pathLoggedOut |
string | "/" |
Post-logout redirect |
Step 4: Configure Password Strategy
Set up password hashing and requirements:
{
auth: {
enabled: true,
jwt: { secret: process.env.JWT_SECRET },
strategies: {
password: {
type: "password",
enabled: true,
config: {
hashing: "bcrypt", // "plain" | "sha256" | "bcrypt"
rounds: 4, // bcrypt rounds (1-10)
minLength: 8, // Minimum password length
},
},
},
},
}
Hashing options:
| Option | Security | Performance | Use Case |
|---|---|---|---|
plain |
None | Fastest | Development only, never production |
sha256 |
Good | Fast | Default, suitable for most cases |
bcrypt |
Best | Slower | Recommended for production |
Step 5: Define Roles
Configure roles for authorization:
{
auth: {
enabled: true,
jwt: { secret: process.env.JWT_SECRET },
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,
is_default: true, // Default role for new registrations
permissions: [
{ permission: "data.posts.read", effect: "allow" },
],
},
},
default_role_register: "user", // Role assigned on registration
},
}
Step 6: Configure Registration
Control user self-registration:
{
auth: {
enabled: true,
allow_register: true, // Enable/disable registration
default_role_register: "user", // Role for new users
entity_name: "users", // User entity name (default: "users")
basepath: "/api/auth", // Auth API base path
},
}
Full Production Example
Complete auth setup with security best practices:
import { serve, type BunBkndConfig } from "bknd/adapter/bun";
import { em, entity, text, date } from "bknd";
const schema = em({
users: entity("users", {
email: text().required().unique(),
name: text(),
avatar: text(),
created_at: date({ default_value: "now" }),
}),
posts: entity("posts", {
title: text().required(),
content: text(),
}),
});
type Database = (typeof schema)["DB"];
declare module "bknd" {
interface DB extends Database {}
}
const config: BunBkndConfig = {
connection: { url: process.env.DB_URL || "file:data.db" },
config: {
data: schema.toJSON(),
auth: {
enabled: true,
basepath: "/api/auth",
entity_name: "users",
allow_register: true,
default_role_register: "user",
// JWT configuration
jwt: {
secret: process.env.JWT_SECRET!,
alg: "HS256",
expires: 604800, // 7 days
issuer: "my-app",
fields: ["id", "email", "role"],
},
// Cookie configuration
cookie: {
secure: process.env.NODE_ENV === "production",
httpOnly: true,
sameSite: "lax",
expires: 604800,
renew: true,
pathSuccess: "/dashboard",
pathLoggedOut: "/login",
},
// Password strategy
strategies: {
password: {
type: "password",
enabled: true,
config: {
hashing: "bcrypt",
rounds: 4,
minLength: 8,
},
},
},
// Roles
roles: {
admin: {
implicit_allow: true,
},
editor: {
implicit_allow: false,
permissions: [
{ permission: "data.posts.read", effect: "allow" },
{ permission: "data.posts.create", effect: "allow" },
{ permission: "data.posts.update", effect: "allow" },
{ permission: "data.posts.delete", effect: "allow" },
],
},
user: {
implicit_allow: false,
is_default: true,
permissions: [
{ permission: "data.posts.read", effect: "allow" },
],
},
},
},
},
options: {
seed: async (ctx) => {
// Create initial admin on first run
const adminExists = await ctx.em.repo("users").findOne({
where: { email: { $eq: "admin@example.com" } },
});
if (!adminExists) {
await ctx.app.module.auth.createUser({
email: "admin@example.com",
password: process.env.ADMIN_PASSWORD || "changeme123",
role: "admin",
});
console.log("Admin user created");
}
},
},
};
serve(config);
Auth Endpoints
After setup, these endpoints are available:
| Method | Path | Description |
|---|---|---|
| POST | /api/auth/password/login |
Login with email/password |
| POST | /api/auth/password/register |
Register new user |
| GET | /api/auth/me |
Get current user |
| POST | /api/auth/logout |
Log out (clear cookie) |
| GET | /api/auth/strategies |
List enabled strategies |
Environment Variables
Recommended env vars for auth:
# .env
JWT_SECRET=your-256-bit-secret-minimum-32-characters-long
ADMIN_PASSWORD=secure-initial-admin-password
Generate a secure secret:
# Generate 64-character random string
openssl rand -hex 32
Development vs Production
| Setting | Development | Production |
|---|---|---|
jwt.secret |
Can use placeholder | Required, strong secret |
cookie.secure |
false |
true (HTTPS only) |
strategies.password.config.hashing |
sha256 |
bcrypt |
allow_register |
true |
Consider false for closed systems |
Dev config shortcut:
const isDev = process.env.NODE_ENV !== "production";
{
auth: {
enabled: true,
jwt: {
secret: isDev ? "dev-secret-not-for-production" : process.env.JWT_SECRET!,
expires: isDev ? 86400 * 30 : 604800, // 30 days dev, 7 days prod
},
cookie: {
secure: !isDev,
},
strategies: {
password: {
type: "password",
config: {
hashing: isDev ? "sha256" : "bcrypt",
},
},
},
},
}
Common Pitfalls
Missing JWT Secret in Production
Problem: Cannot sign JWT without secret error
Fix: Set JWT secret via environment variable:
{
auth: {
jwt: {
secret: process.env.JWT_SECRET, // Never hardcode in production
},
},
}
Cookie Not Set (HTTPS Issues)
Problem: Auth cookie not set in browser
Fix: Set secure: false for local development:
{
auth: {
cookie: {
secure: process.env.NODE_ENV === "production", // false for localhost
},
},
}
Role Not Found
Problem: Role "admin" not found when creating users
Fix: Define roles before referencing them:
{
auth: {
roles: {
admin: { implicit_allow: true }, // Define first
user: { implicit_allow: false },
},
default_role_register: "user", // Now can reference
},
}
Registration Disabled
Problem: Registration not allowed error
Fix: Enable registration:
{
auth: {
allow_register: true, // Default is true, but check if explicitly disabled
},
}
Weak Password Hashing
Problem: Using plain or sha256 in production
Fix: Use bcrypt for production:
{
auth: {
strategies: {
password: {
config: {
hashing: "bcrypt",
rounds: 4, // Balance security and performance
},
},
},
},
}
Verification
After setup, verify auth works:
1. Check enabled strategies:
curl http://localhost:7654/api/auth/strategies
2. Register a test user:
curl -X POST http://localhost:7654/api/auth/password/register \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com", "password": "password123"}'
3. Login:
curl -X POST http://localhost:7654/api/auth/password/login \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com", "password": "password123"}'
4. Check current user (with token):
curl http://localhost:7654/api/auth/me \
-H "Authorization: Bearer <token-from-login>"
Security Checklist
Before deploying to production:
- [ ] Set strong
jwt.secret(256-bit minimum) - [ ] Use
hashing: "bcrypt"for password strategy - [ ] Set
cookie.secure: true(HTTPS only) - [ ] Set
cookie.httpOnly: true(default) - [ ] Set
cookie.sameSite: "lax"or"strict" - [ ] Configure
jwt.expires(don't leave unlimited) - [ ] Review
allow_registersetting - [ ] Create admin user via seed (not via public registration)
- [ ] Store secrets in environment variables
DOs and DON'Ts
DO:
- Use environment variables for secrets
- Use bcrypt hashing in production
- Set JWT expiry times
- Define roles before assigning them
- Test auth flow after configuration changes
DON'T:
- Hardcode JWT secrets in code
- Use
plainhashing in production - Skip setting
cookie.securein production - Leave registration open if not needed
- Forget to create initial admin user
Related Skills
- bknd-create-user - Create user accounts programmatically
- bknd-login-flow - Implement login/logout functionality
- bknd-registration - Set up user registration flows
- bknd-oauth-setup - Configure OAuth providers (Google, GitHub)
- bknd-create-role - Define roles for authorization
- bknd-session-handling - Manage user sessions
You Might Also Like
Related Skills

coding-agent
Run Codex CLI, Claude Code, OpenCode, or Pi Coding Agent via background process for programmatic control.
openclaw
add-uint-support
Add unsigned integer (uint) type support to PyTorch operators by updating AT_DISPATCH macros. Use when adding support for uint16, uint32, uint64 types to operators, kernels, or when user mentions enabling unsigned types, barebones unsigned types, or uint support.
pytorch
at-dispatch-v2
Convert PyTorch AT_DISPATCH macros to AT_DISPATCH_V2 format in ATen C++ code. Use when porting AT_DISPATCH_ALL_TYPES_AND*, AT_DISPATCH_FLOATING_TYPES*, or other dispatch macros to the new v2 API. For ATen kernel files, CUDA kernels, and native operator implementations.
pytorch
skill-writer
Guide users through creating Agent Skills for Claude Code. Use when the user wants to create, write, author, or design a new Skill, or needs help with SKILL.md files, frontmatter, or skill structure.
pytorch
implementing-jsc-classes-cpp
Implements JavaScript classes in C++ using JavaScriptCore. Use when creating new JS classes with C++ bindings, prototypes, or constructors.
oven-sh
implementing-jsc-classes-zig
Creates JavaScript classes using Bun's Zig bindings generator (.classes.ts). Use when implementing new JS APIs in Zig with JSC integration.
oven-sh