bknd-setup-auth

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.

1estrelas
0forks
Atualizado 1/21/2026
SKILL.md
readonlyread-only
name
bknd-setup-auth
description

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 users entity
  • 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_register setting
  • [ ] 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 plain hashing in production
  • Skip setting cookie.secure in 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

coding-agent

179Kdev-codegen

Run Codex CLI, Claude Code, OpenCode, or Pi Coding Agent via background process for programmatic control.

openclaw avataropenclaw
Obter
add-uint-support

add-uint-support

97Kdev-codegen

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 avatarpytorch
Obter
at-dispatch-v2

at-dispatch-v2

97Kdev-codegen

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 avatarpytorch
Obter
skill-writer

skill-writer

97Kdev-codegen

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 avatarpytorch
Obter

Implements JavaScript classes in C++ using JavaScriptCore. Use when creating new JS classes with C++ bindings, prototypes, or constructors.

oven-sh avataroven-sh
Obter

Creates JavaScript classes using Bun's Zig bindings generator (.classes.ts). Use when implementing new JS APIs in Zig with JSC integration.

oven-sh avataroven-sh
Obter