实骨 shigu
  • Joined on 2026-08-12

@tmxk/iam (0.3.2)

Published 2026-08-23 13:50:24 +00:00 by shigu

Installation

@tmxk:registry=https://gitea.cspy.site/api/packages/shigu/npm/
npm install @tmxk/iam@0.3.2
"@tmxk/iam": "0.3.2"

About this package

可嵌入 Node.js / Next.js 的用户、组织、角色权限与外部身份管理 SDK

tmxk-iam

tmxk-iam 是嵌入 Node.js / Next.js 业务系统的身份与访问管理 SDK。它管理用户、外部身份、企业成员、角色、权限、数据范围、会话和审计;微信 AppSecret 与 OAuth 流程仍由独立的 tmxk-identity 负责。

设计文档:微信自助登记与管理员审核设计。

定位

  • 核心是 headless SDK,业务系统可以完全自定义 UI。
  • 提供稳定的业务 API,不向业务层暴露 Drizzle 或 IAM 表。
  • 第一版使用 SQLite,存储接口可以继续实现 PostgreSQL 方言。
  • 权限由业务系统固定定义;角色可以预设,也可以由管理员自定义。
  • 角色可以绑定到系统、企业、项目或本人范围。

安装与初始化

pnpm add @tmxk/iam
import { createIam } from "@tmxk/iam";
import { createSqliteStorage } from "@tmxk/iam/sqlite";
import { createNextIam } from "@tmxk/iam/next";

export const iam = createIam({
  applicationId: "lab-flow",
  storage: createSqliteStorage({ filename: "./data/iam.db" }),
  permissions: [
    { key: "project.read", name: "查看项目", resourceType: "project", actions: ["read"] },
    { key: "project.manage", name: "管理项目", resourceType: "project", actions: ["create", "update", "delete"] },
  ],
  presetRoles: [
    { key: "organization_admin", name: "企业管理员", permissions: ["project.read", "project.manage"] },
    { key: "viewer", name: "查看者", permissions: ["project.read"] },
  ],
  identity: {
    issuer: process.env.TMXK_IDENTITY_ISSUER!,
    providerConfigId: process.env.TMXK_IDENTITY_PROVIDER_CONFIG_ID!,
    redirectUri: process.env.TMXK_IDENTITY_REDIRECT_URI!,
    firstLogin: "pending",
  },
});

export const nextIam = createNextIam(iam, {
  stateSecret: process.env.TMXK_IAM_STATE_SECRET!,
});

应用启动时执行一次:

await iam.initialize();

微信登录

登录入口 Route Handler:

import { nextIam } from "@/lib/iam";

export async function GET(request: Request) {
  const returnTo = new URL(request.url).searchParams.get("returnTo") ?? "/";
  return Response.redirect(nextIam.createLoginUrl({ returnTo }));
}

回调 Route Handler:

import { nextIam } from "@/lib/iam";

export const runtime = "nodejs";
export async function GET(request: Request) {
  return nextIam.handleCallback(request);
}

在 tmxk-identity 的对应 Provider 中,需要将 systemKey 配成这里的 applicationId,并将业务系统回调地址加入精确白名单。

首次登录策略:

  • pending:创建待审核用户,管理员确认企业和角色后启用,推荐默认值。
  • auto-create:自动创建并启用用户,适合低风险内部应用。
  • binding-only:只允许已经绑定的微信登录。

账号密码

await iam.passwords.set(userId, "admin", "至少十个字符的密码");
const user = await iam.passwords.authenticate(username, password);
const sessionToken = await iam.sessions.create(user.id);

账号统一按小写保存;密码使用带独立随机盐的 scrypt 哈希。连续失败 5 次后锁定 15 分钟,数据库不保存明文密码。

绑定微信

绑定入口必须先校验当前业务会话,不能接受浏览器随意指定的用户 ID:

const principal = await nextIam.requirePrincipal(request);
const url = nextIam.createLoginUrl({
  mode: "bind",
  userId: principal.userId,
  returnTo: "/settings/account",
});
return Response.redirect(url);

回调仍复用登录回调。SDK 会拒绝把一个已经绑定的微信身份绑定给另一用户。

默认不会仅凭 unionId 自动合并用户;确认所有相关公众号属于同一微信开放平台主体后,才可以显式设置 linkByUnionId: true。

权限和数据范围

单条资源校验:

await iam.access.require({
  principal,
  action: "project.read",
  resource: { type: "project", organizationId, projectId, ownerId },
});

列表查询先取得抽象数据范围,再由业务 Repository 编译成自身 ORM 的查询条件:

const scope = await iam.access.queryScope({ principal, action: "project.read" });
return projectRepository.list({ scope });

IAM 不拥有项目、实验、订单等业务表;它只计算当前人员有权访问哪些企业、项目或本人数据。

UI

当前包是 SDK,不强制任何管理界面。业务系统通过这些 API 实现自己的用户管理、企业管理、角色编辑、授权和微信绑定页面。后续可以增加独立的 @tmxk/iam-react 无样式组件,以及可复制的 Next.js 管理页面模板,而不让核心包绑定特定视觉样式。

安全边界

  • 微信 AppSecret 只保存在 tmxk-identity。
  • 浏览器只接触一次性 Code,业务系统后端完成交换。
  • systemKey 必须与 IAM 的 applicationId 一致。
  • returnTo 只允许站内绝对路径,避免开放重定向。
  • Session 只以 SHA-256 哈希形式存库,Cookie 使用 HttpOnly、SameSite=Lax、生产环境 Secure。
  • 不向业务层导出数据库连接;所有权限判断都必须在服务端完成。

Dependencies

Dependencies

ID Version
drizzle-orm ^0.45.2

Development Dependencies

ID Version
@types/node ^26.0.0
typescript ^7.0.2

Peer Dependencies

ID Version
next >=16
Details
npm
2026-08-23 13:50:24 +00:00
26
28 KiB
Assets (1)
iam-0.3.2.tgz 28 KiB
Versions (17) View all
0.7.0 2026-09-13
0.6.1 2026-09-07
0.5.0 2026-09-06
0.4.8 2026-09-06
0.4.7 2026-08-27