Token导航 LogoToken导航

Agent Skill · 开发

bknd-protect-endpoint(bknd 保护端点)

用于辅助 API 设计、接口文档、请求响应结构和服务集成说明。它适合让 Agent 梳理 endpoint、生成 OpenAPI 草稿、检查字段命名、整理错误码或辅助前后端联调。使用时需要确认真实业务语义、鉴权方式、分页和错误处理规则;涉及生成接口文档时,应避免凭空补字段,最好从现有代码、schema 或接口样例中提取事实。

bknd-protect-endpoint 是开发类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证信息还不明确,第三方扫描有提醒项,按安装量在开发类的 21,746 个 Skill 里排第 18,691。

总安装 321周安装 13GitHub Stars 3下载 101核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:bknd-protect-endpoint(bknd 保护端点)
来源仓库:https://github.com/cameronapak/bknd-skills
仓库路径:skills/bknd-protect-endpoint
安装命令:
npx skills add https://github.com/cameronapak/bknd-skills --skill bknd-protect-endpoint
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/cameronapak/bknd-skills --skill bknd-protect-endpoint

这个 Skill 是做什么的

开发类 Skill 面向写代码、改代码和排查问题,一般会读写仓库文件,部分还会执行构建或测试命令。

用于为 API 端点添加身份验证和权限控制保护。支持角色检查和自定义触发器鉴权逻辑实现。适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。适用于插件路由和外部服务集成时的安全封装。需启用 auth 和 guard 功能并定义相关角色。

bknd-protect-endpoint 属于开发类 Skill,可作为该场景下的辅助能力补充。

来源仓库内路径:skills/bknd-protect-endpoint

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/cameronapak/bknd-skills --skill bknd-protect-e…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

bknd-protect-endpoint 怎么安装?

在终端执行 npx skills add https://github.com/cameronapak/bknd-skills --skill bknd-protect-endpoint。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

bknd-protect-endpoint 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

bknd-protect-endpoint 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

bknd-protect-endpoint 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

bknd-protect-endpoint 用的人多吗?

公开数据是累计安装 321 次,近一周安装 13 次,下载 101 次,GitHub 3 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

bknd-protect-endpoint 安全吗?

第三方扫描有提醒项,说明存在需要人工确认的行为。安装前请打开源码看清它读了什么、联网到哪里。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方扫描存在提醒项或未通过项,需要人工逐条确认。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理开发类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

开发类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
skill-vetter

技能审查员 · OpenClaw

8,779,018986操作浏览器MIT-0通过
ontology

本体论 · OpenClaw

6,636,960576只读MIT-0通过
entra-app-registration

内部应用程序注册 · Codex、Claude

6,631,872764接触敏感数据未明确通过
entra-app-registration

内部应用程序注册 · Codex、Claude

3,757,680201接触敏感数据未明确通过
nano-banana-pro

纳米香蕉专业版 · OpenClaw

3,526,898364接触敏感数据MIT-0有提醒
bknd-protect-endpoint

bknd 保护端点 · 本页

3213需要联网未明确有提醒

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Protect Endpoint

Secure specific API endpoints with authentication and authorization checks.

Prerequisites

  • Bknd project with code-first configuration
  • Auth enabled (auth: {enabled: true})
  • Guard enabled for authorization (guard: {enabled: true})
  • Roles defined (see bknd-create-role)

When to Use UI Mode

  • Viewing registered routes: Admin Panel > System > Debug
  • Inspecting role permissions

Note: Endpoint protection requires code mode. UI is read-only.

When to Use Code Mode

  • Creating protected custom endpoints
  • Adding auth checks to HTTP triggers
  • Building protected plugin routes
  • Implementing endpoint-specific permissions

Code Approach

Understanding Endpoint Types

Bknd has several endpoint types to protect:

TypePath PatternHow to Protect
Data API/api/data/*Guard permissions (automatic)
Auth API/api/auth/*Built-in protection
Media API/api/media/*Guard permissions (automatic)
HTTP TriggersCustom pathsManual auth check
Plugin RoutesCustom pathsManual auth check

Step 1: Protect HTTP Trigger (Flow)

Add authentication to a custom endpoint via FunctionTask:

import { serve } from "bknd/adapter/bun";
import { Flow, HttpTrigger, FunctionTask } from "bknd";

// Protected endpoint flow
const protectedFlow = new Flow("protected-endpoint", [
  new FunctionTask({
    name: "checkAuth",
    handler: async (input, ctx) => {
      // ctx.app gives access to modules
      const authModule = ctx.app.modules.get("auth");
      const user = await authModule.authenticator.getUserFromRequest(input);

      if (!user) {
        throw new Response(JSON.stringify({ error: "Unauthorized" }), {
          status: 401,
          headers: { "Content-Type": "application/json" },
        });
      }

      // Pass user to next task
      return { user, body: await input.json() };
    },
  }),
  new FunctionTask({
    name: "processRequest",
    handler: async (input) => {
      // input contains { user, body } from previous task
      return {
        message: `Hello ${input.user.email}`,
        data: input.body,
      };
    },
  }),
]);

protectedFlow.setTrigger(
  new HttpTrigger({
    path: "/api/custom/protected",
    method: "POST",
    respondWith: "processRequest",
  })
);

serve({
  connection: { url: "file:data.db" },
  config: {
    flows: {
      flows: [protectedFlow],
    },
  },
});

Step 2: Protect Plugin Route

Add auth check in plugin's onServerInit:

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

const protectedPlugin = createPlugin({
  name: "protected-routes",

  onServerInit: (server) => {
    // Protected endpoint
    server.post("/api/custom/data", async (c) => {
      // Get app from context
      const app = c.get("app");
      const authModule = app.modules.get("auth");

      // Resolve user from request
      const user = await authModule.authenticator.getUserFromRequest(c.req.raw);

      if (!user) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      // Proceed with protected logic
      const body = await c.req.json();
      return c.json({
        message: "Protected data",
        user: user.email,
        received: body,
      });
    });

    // Public endpoint (no auth check)
    server.get("/api/custom/public", (c) => {
      return c.json({ message: "Public data" });
    });
  },
});

serve({
  connection: { url: "file:data.db" },
  plugins: [protectedPlugin],
});

Step 3: Role-Based Endpoint Protection

Check user's role for specific permissions:

const roleProtectedPlugin = createPlugin({
  name: "role-protected",

  onServerInit: (server) => {
    // Admin-only endpoint
    server.delete("/api/admin/users/:id", async (c) => {
      const app = c.get("app");
      const authModule = app.modules.get("auth");
      const user = await authModule.authenticator.getUserFromRequest(c.req.raw);

      // Check authentication
      if (!user) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      // Check role
      if (user.role !== "admin") {
        return c.json({ error: "Forbidden: Admin role required" }, 403);
      }

      // Proceed with admin action
      const userId = c.req.param("id");
      // ... delete user logic
      return c.json({ deleted: userId });
    });
  },
});

Step 4: Permission-Based Protection with Guard

Use Guard for granular permission checks:

import { createPlugin, DataPermissions } from "bknd";

const guardProtectedPlugin = createPlugin({
  name: "guard-protected",

  onServerInit: (server) => {
    server.post("/api/custom/sync", async (c) => {
      const app = c.get("app");
      const authModule = app.modules.get("auth");
      const guard = authModule.guard;

      const user = await authModule.authenticator.getUserFromRequest(c.req.raw);

      if (!user) {
        return c.json({ error: "Unauthorized" }, 401);
      }

      // Check specific permission using Guard
      try {
        guard.granted(
          DataPermissions.databaseSync,  // Permission to check
          { role: user.role },           // User context
          {}                             // Permission context
        );
      } catch (error) {
        return c.json({
          error: "Forbidden",
          message: error.message,
        }, 403);
      }

      // User has permission - proceed
      return c.json({ status: "sync started" });
    });
  },
});

Step 5: Entity-Specific Permission Check

Check permissions for specific entity operations:

server.post("/api/custom/posts/batch", async (c) => {
  const app = c.get("app");
  const authModule = app.modules.get("auth");
  const guard = authModule.guard;

  const user = await authModule.authenticator.getUserFromRequest(c.req.raw);

  if (!user) {
    return c.json({ error: "Unauthorized" }, 401);
  }

  // Check create permission for posts entity
  try {
    guard.granted(
      DataPermissions.entityCreate,
      { role: user.role },
      { entity: "posts" }  // Entity-specific context
    );
  } catch (error) {
    return c.json({
      error: "Cannot create posts",
      message: error.message,
    }, 403);
  }

  // Proceed with batch creation
  const body = await c.req.json();
  // ... create posts
  return c.json({ created: body.length });
});

Step 6: Reusable Auth Middleware

Create a helper for consistent auth checks:

// auth-middleware.ts
type AuthContext = {
  user: any;
  role: string;
};

export async function requireAuth(
  c: any,
  app: any
): Promise<AuthContext | Response> {
  const authModule = app.modules.get("auth");
  const user = await authModule.authenticator.getUserFromRequest(c.req.raw);

  if (!user) {
    return c.json({ error: "Unauthorized" }, 401);
  }

  return { user, role: user.role };
}

export async function requireRole(
  c: any,
  app: any,
  allowedRoles: string[]
): Promise<AuthContext | Response> {
  const result = await requireAuth(c, app);

  if (result instanceof Response) {
    return result;
  }

  if (!allowedRoles.includes(result.role)) {
    return c.json({
      error: "Forbidden",
      required: allowedRoles,
      current: result.role,
    }, 403);
  }

  return result;
}

// Usage in plugin
server.get("/api/reports/admin", async (c) => {
  const app = c.get("app");
  const auth = await requireRole(c, app, ["admin", "manager"]);

  if (auth instanceof Response) return auth;

  // auth.user available
  return c.json({ reports: [] });
});

Step 7: Protecting Flow with Auth Task

Create reusable auth task for Flows:

import { Flow, HttpTrigger, FunctionTask } from "bknd";

// Reusable auth task
const authTask = new FunctionTask({
  name: "requireAuth",
  handler: async (input, ctx) => {
    const authModule = ctx.app.modules.get("auth");
    const user = await authModule.authenticator.getUserFromRequest(input);

    if (!user) {
      throw new Response(
        JSON.stringify({ error: "Unauthorized" }),
        { status: 401, headers: { "Content-Type": "application/json" } }
      );
    }

    return { request: input, user };
  },
});

// Reusable role check task
const requireAdmin = new FunctionTask({
  name: "requireAdmin",
  handler: async (input) => {
    if (input.user.role !== "admin") {
      throw new Response(
        JSON.stringify({ error: "Admin required" }),
        { status: 403, headers: { "Content-Type": "application/json" } }
      );
    }
    return input;
  },
});

// Protected flow
const adminFlow = new Flow("admin-action", [
  authTask,
  requireAdmin,
  new FunctionTask({
    name: "performAction",
    handler: async (input) => {
      return { success: true, admin: input.user.email };
    },
  }),
]);

adminFlow.setTrigger(
  new HttpTrigger({
    path: "/api/admin/action",
    method: "POST",
    respondWith: "performAction",
  })
);

Common Patterns

Optional Auth (Public with Extra Features)

server.get("/api/posts", async (c) => {
  const app = c.get("app");
  const authModule = app.modules.get("auth");
  const api = app.getApi();

  // Try to get user (may be null)
  const user = await authModule.authenticator.getUserFromRequest(c.req.raw);

  if (user) {
    // Authenticated: show all posts including drafts
    const posts = await api.data.readMany("posts", {
      where: {
        $or: [
          { status: "published" },
          { author_id: user.id },
        ],
      },
    });
    return c.json(posts.data);
  } else {
    // Anonymous: show only published
    const posts = await api.data.readMany("posts", {
      where: { status: "published" },
    });
    return c.json(posts.data);
  }
});

Rate-Limited Protected Endpoint

const rateLimits = new Map<string, { count: number; reset: number }>();

server.post("/api/expensive-operation", async (c) => {
  const app = c.get("app");
  const authModule = app.modules.get("auth");
  const user = await authModule.authenticator.getUserFromRequest(c.req.raw);

  if (!user) {
    return c.json({ error: "Unauthorized" }, 401);
  }

  // Simple rate limiting by user
  const key = `user:${user.id}`;
  const now = Date.now();
  const limit = rateLimits.get(key);

  if (limit && limit.reset > now && limit.count >= 10) {
    return c.json({
      error: "Rate limit exceeded",
      retryAfter: Math.ceil((limit.reset - now) / 1000),
    }, 429);
  }

  // Update rate limit
  if (!limit || limit.reset < now) {
    rateLimits.set(key, { count: 1, reset: now + 60000 });
  } else {
    limit.count++;
  }

  // Proceed
  return c.json({ result: "success" });
});

API Key Authentication

For service-to-service or external API access:

const API_KEYS = new Set([
  process.env.SERVICE_API_KEY,
  process.env.PARTNER_API_KEY,
]);

server.post("/api/webhook/external", async (c) => {
  const apiKey = c.req.header("X-API-Key");

  if (!apiKey || !API_KEYS.has(apiKey)) {
    return c.json({ error: "Invalid API key" }, 401);
  }

  // Proceed with webhook handling
  const body = await c.req.json();
  return c.json({ received: true });
});

Verification

1. Test Unauthenticated Access

# Should return 401
curl -X POST http://localhost:7654/api/custom/protected \
  -H "Content-Type: application/json" \
  -d '{"test": "data"}'

2. Test Authenticated Access

# Login first
TOKEN=$(curl -s -X POST http://localhost:7654/api/auth/password/login \
  -H "Content-Type: application/json" \
  -d '{"email": "user@test.com", "password": "pass123"}' | jq -r '.token')

# Access protected endpoint
curl -X POST http://localhost:7654/api/custom/protected \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"test": "data"}'

3. Test Role Restriction

# Login as non-admin
TOKEN=$(curl -s -X POST http://localhost:7654/api/auth/password/login \
  -H "Content-Type: application/json" \
  -d '{"email": "user@test.com", "password": "pass123"}' | jq -r '.token')

# Should return 403
curl -X DELETE http://localhost:7654/api/admin/users/1 \
  -H "Authorization: Bearer $TOKEN"

4. Verify with Admin Role

# Login as admin
ADMIN_TOKEN=$(curl -s -X POST http://localhost:7654/api/auth/password/login \
  -H "Content-Type: application/json" \
  -d '{"email": "admin@test.com", "password": "admin123"}' | jq -r '.token')

# Should succeed
curl -X DELETE http://localhost:7654/api/admin/users/1 \
  -H "Authorization: Bearer $ADMIN_TOKEN"

Common Pitfalls

User Always Null

Problem: getUserFromRequest() returns null even with valid token

Fix: Ensure token is sent correctly:

// Header auth
fetch("/api/custom/protected", {
  headers: { "Authorization": `Bearer ${token}` }
});

// OR cookie auth (if using cookies)
fetch("/api/custom/protected", {
  credentials: "include"  // Send cookies
});

Guard Not Available

Problem: authModule.guard is undefined

Fix: Ensure guard is enabled:

{
  auth: {
    enabled: true,
    guard: { enabled: true },  // Required!
  },
}

Permission Check Throws Wrong Error

Problem: Guard throws unexpected error type

Fix: Catch specific exception:

import { GuardPermissionsException } from "bknd";

try {
  guard.granted(permission, context, permContext);
} catch (error) {
  if (error instanceof GuardPermissionsException) {
    return c.json({ error: error.message }, 403);
  }
  throw error;  // Re-throw unexpected errors
}

CORS Blocking Auth Header

Problem: Preflight fails for Authorization header

Fix: Configure CORS:

serve({
  // ...
  config: {
    server: {
      cors: {
        origin: ["http://localhost:3000"],
        credentials: true,
        allowHeaders: ["Authorization", "Content-Type"],
      },
    },
  },
});

Flow Task Doesn't Have App Context

Problem: ctx.app undefined in FunctionTask

Fix: Access via execution context:

new FunctionTask({
  name: "withApp",
  handler: async (input, ctx) => {
    // ctx.app is available in FunctionTask
    const app = ctx.app;
    // ...
  },
});

DOs and DON'Ts

DO:

  • Always check auth before processing sensitive requests
  • Use Guard for permission checks (consistent with Bknd's system)
  • Return appropriate HTTP status codes (401, 403)
  • Create reusable auth helpers for consistency
  • Log auth failures for security monitoring

DON'T:

  • Trust client-provided user IDs without verification
  • Expose detailed error messages about auth failures
  • Skip auth checks assuming "internal" endpoints are safe
  • Store sensitive data in JWT payload (use user ID only)
  • Forget to handle both header and cookie auth methods

Related Skills

  • bknd-create-role - Define roles for authorization
  • bknd-assign-permissions - Configure role permissions
  • bknd-public-vs-auth - Public vs authenticated access
  • bknd-row-level-security - Data-level access control
  • bknd-custom-endpoint - Create custom API endpoints

平台分布

Codex

34.75%
按下载量换算35

Claude

28.63%
按下载量换算29

Cursor

18.71%
按下载量换算19

Gemini CLI

9.28%
按下载量换算9

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

可疑

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在提醒或未通过项,安装前请自行复核。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills