Back to list
cameronapak

bknd-create-role

by cameronapak

A no-build, un-bloated stack built upon Web Standards that feels freeing to use and can be deployed anywhere.

23🍴 2📅 Jan 21, 2026

SKILL.md


name: bknd-create-role description: Use when defining a new role in Bknd authorization system. Covers role properties (implicit_allow, is_default, permissions), permission assignment, role hierarchies, and common role patterns (admin, editor, viewer, anonymous).

Create Role

Define a new role in Bknd's authorization system to control user access.

Prerequisites

  • Bknd project initialized with code-first configuration
  • Auth enabled (auth: { enabled: true })
  • Guard enabled for authorization (guard: { enabled: true })

When to Use UI Mode

  • Viewing existing roles
  • Quick toggle of role settings

UI steps: Admin Panel > Auth > Roles

Note: Role creation requires code mode. UI only shows existing roles.

When to Use Code Mode

  • Creating new roles
  • Setting role permissions
  • Configuring default roles
  • Setting up role hierarchies

Code Approach

Step 1: Enable Guard

Roles require the guard system to be enabled:

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,
      guard: { enabled: true },  // Required for roles
      roles: {
        // Roles defined here
      },
    },
  },
});

Step 2: Define a Basic Role

Create a role with explicit permissions:

{
  auth: {
    enabled: true,
    guard: { enabled: true },
    roles: {
      viewer: {
        implicit_allow: false,  // Deny by default
        permissions: [
          "data.entity.read",   // Grant read access only
        ],
      },
    },
  },
}

Role Properties

PropertyTypeDefaultDescription
implicit_allowbooleanfalseAllow all unless denied
is_defaultbooleanfalseUse when user has no role
permissionsarray[]Permissions granted to role

Step 3: Create Admin Role (Full Access)

Grant full access with implicit_allow:

{
  roles: {
    admin: {
      implicit_allow: true,  // Can do everything
    },
  },
}

Warning: implicit_allow: true grants ALL permissions. Use only for admin roles.

Step 4: Create Editor Role (Partial Access)

Grant specific CRUD permissions:

{
  roles: {
    editor: {
      implicit_allow: false,
      permissions: [
        "data.entity.read",
        "data.entity.create",
        "data.entity.update",
        // No delete permission
      ],
    },
  },
}

Step 5: Create Default Role

Set a role for users without assigned role:

{
  roles: {
    anonymous: {
      is_default: true,       // Applied when no role
      implicit_allow: false,
      permissions: [
        "data.entity.read",   // Read-only access
      ],
    },
  },
}

Note: Only ONE role can have is_default: true.

Step 6: Set Registration Role

Assign role to newly registered users:

{
  auth: {
    enabled: true,
    default_role_register: "user",  // Role for new registrations
    roles: {
      user: {
        implicit_allow: false,
        permissions: ["data.entity.read"],
      },
    },
  },
}

Available Permissions

PermissionDescription
data.entity.readRead any entity records
data.entity.createCreate records in any entity
data.entity.updateUpdate records in any entity
data.entity.deleteDelete records from any entity
data.database.syncSync database schema
data.raw.queryExecute raw SELECT queries
data.raw.mutateExecute raw INSERT/UPDATE/DELETE

Common Role Patterns

Multi-Tier Access System

{
  auth: {
    enabled: true,
    guard: { enabled: true },
    default_role_register: "user",
    roles: {
      // Full access
      admin: {
        implicit_allow: true,
      },

      // Content management
      editor: {
        implicit_allow: false,
        permissions: [
          "data.entity.read",
          "data.entity.create",
          "data.entity.update",
          "data.entity.delete",
        ],
      },

      // Create and read
      contributor: {
        implicit_allow: false,
        permissions: [
          "data.entity.read",
          "data.entity.create",
        ],
      },

      // Authenticated read-only
      user: {
        implicit_allow: false,
        permissions: [
          "data.entity.read",
        ],
      },

      // Unauthenticated/guest
      anonymous: {
        is_default: true,
        implicit_allow: false,
        permissions: [
          "data.entity.read",
        ],
      },
    },
  },
}

Closed System (No Public Access)

{
  auth: {
    enabled: true,
    guard: { enabled: true },
    allow_register: false,  // Disable self-registration
    roles: {
      admin: {
        implicit_allow: true,
      },
      member: {
        implicit_allow: false,
        permissions: [
          "data.entity.read",
          "data.entity.create",
          "data.entity.update",
        ],
      },
      // No default role - unauthenticated users get NO access
    },
  },
}

API Consumer Role

{
  roles: {
    api_client: {
      implicit_allow: false,
      permissions: [
        "data.entity.read",
        "data.entity.create",
        // No update/delete - API clients create data only
      ],
    },
  },
}

Permission Effects

Use extended format for allow/deny effects:

{
  roles: {
    moderator: {
      implicit_allow: false,
      permissions: [
        { permission: "data.entity.read", effect: "allow" },
        { permission: "data.entity.update", effect: "allow" },
        { permission: "data.entity.delete", effect: "deny" },  // Explicit deny
      ],
    },
  },
}

Role Assignment

Assign During User Creation (Seed)

{
  options: {
    seed: async (ctx) => {
      await ctx.app.module.auth.createUser({
        email: "admin@example.com",
        password: "secure-password",
        role: "admin",  // Assign admin role
      });
    },
  },
}

Assign During Registration

{
  auth: {
    default_role_register: "user",  // All registrations get "user" role
  },
}

Update User Role (API)

const api = getApi(app);

// Update user's role
await api.data.updateOne("users", userId, {
  role: "editor",
});

Verification

Test role permissions:

1. Create user with role:

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

2. Login and get token:

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

3. Test permission (should succeed for read):

curl http://localhost:7654/api/data/posts \
  -H "Authorization: Bearer <token>"

4. Test denied permission (should fail for delete if not allowed):

curl -X DELETE http://localhost:7654/api/data/posts/1 \
  -H "Authorization: Bearer <token>"
# Returns 403 if delete not in permissions

Common Pitfalls

No Default Role

Problem: User has no role error for unauthenticated users

Fix: Set a default role:

{
  roles: {
    anonymous: {
      is_default: true,
      permissions: ["data.entity.read"],
    },
  },
}

Multiple Default Roles

Problem: Unpredictable behavior with multiple is_default: true

Fix: Only ONE role should be default:

{
  roles: {
    user: { is_default: true },    // Only one!
    guest: { /* no is_default */ },
  },
}

Role Not Found

Problem: Role "admin" not found when assigning

Fix: Define role before referencing:

{
  auth: {
    roles: {
      admin: { implicit_allow: true },  // Define first
    },
    default_role_register: "admin",     // Then reference
  },
}

Guard Not Enabled

Problem: Roles defined but permissions not enforced

Fix: Enable the guard:

{
  auth: {
    enabled: true,
    guard: { enabled: true },  // Required!
    roles: { /* ... */ },
  },
}

Implicit Allow Overuse

Problem: Using implicit_allow: true on non-admin roles

Fix: Be explicit about permissions:

// WRONG - too permissive
{
  roles: {
    editor: { implicit_allow: true },
  },
}

// CORRECT - explicit permissions
{
  roles: {
    editor: {
      implicit_allow: false,
      permissions: [
        "data.entity.read",
        "data.entity.create",
        "data.entity.update",
      ],
    },
  },
}

DOs and DON'Ts

DO:

  • Enable guard when using roles
  • Use implicit_allow: false for non-admin roles
  • Set one default role for unauthenticated access
  • Define roles before referencing them
  • Test each role's permissions after creation

DON'T:

  • Use implicit_allow: true for non-admin roles
  • Set multiple roles as default
  • Forget to enable guard
  • Grant data.raw.* permissions to untrusted roles
  • Assume roles work without guard enabled
  • bknd-setup-auth - Initialize authentication system
  • bknd-assign-permissions - Configure detailed permissions with policies
  • bknd-row-level-security - Implement row-level access control
  • bknd-protect-endpoint - Secure specific endpoints
  • bknd-public-vs-auth - Configure public vs authenticated access

Score

Total Score

75/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon