# IAM Service

身份与访问管理服务（Identity and Access Management）。

> 版本：v0.2.0（对齐 coord-final-decisions I1-I8 + president §2.15/§2.16/§3.2/§5.5）
> 详细设计见 [02-architecture-design.md](./docs/02-architecture-design.md)

## 职责

- 用户注册 / 登录 / 刷新令牌 / 登出
- JWT RS256 签发（access 15min + refresh 7day，jti 黑名单）
- 角色（Role）与权限（Permission）管理（三层角色模型：system/organization/temporary）
- DataScope 6 级数据范围（self/subject/class/grade/school/all）
- 视口（Viewport）配置查询
- 学生-家长关系管理（`GetChildrenByParent`）
- 审计日志（`iam_user_audit_log` + `AuditCreated` 事件）
- JWKS 公钥暴露（供 api-gateway 验签）
- 双入口：REST `/v1/iam/*` + gRPC 50052（package `next_edu_cloud.iam.v1`，12 RPC）

## 技术栈

- NestJS 10 + TypeScript（ESM，NodeNext）
- Drizzle ORM + MySQL（10 张表）
- bcrypt 密码哈希（cost ≥ 12）
- jsonwebtoken RS256（本地文件密钥）
- ioredis（权限缓存 TTL 5min + token jti 黑名单）
- kafkajs + shared-ts OutboxModule（事务性事件发布）
- @nestjs/microservices + @grpc/grpc-js（gRPC server）
- pino 日志 / prom-client 指标 / OpenTelemetry 链路

## 端口

| 服务     | 端口  | 备注                     |
| -------- | ----- | ------------------------ |
| iam HTTP | 3002  | REST 入口（`PORT`）      |
| iam gRPC | 50052 | gRPC 入口（`GRPC_PORT`） |

## 环境变量

| 变量                          | 说明                           | 默认值           |
| ----------------------------- | ------------------------------ | ---------------- |
| `PORT`                        | HTTP 端口                      | `3002`           |
| `GRPC_PORT`                   | gRPC 端口                      | `50052`          |
| `DATABASE_URL`                | MySQL 连接串                   | —                |
| `REDIS_URL`                   | Redis 连接串                   | —                |
| `IAM_PRIVATE_KEY_PATH`        | RS256 私钥文件路径             | —                |
| `IAM_PUBLIC_KEY_PATH`         | RS256 公钥文件路径             | —                |
| `JWT_ISSUER`                  | JWT 签发者                     | `next-edu-cloud` |
| `JWT_AUDIENCE`                | JWT 受众                       | `next-edu-cloud` |
| `JWT_KEY_ID`                  | 密钥 ID（kid，用于 JWKS 轮换） | `iam-rs256-v1`   |
| `ACCESS_TOKEN_TTL`            | access_token TTL               | `15m`            |
| `REFRESH_TOKEN_TTL_DAYS`      | refresh_token TTL（天）        | `7`              |
| `KAFKA_BROKERS`               | Kafka broker 列表（逗号分隔）  | —                |
| `KAFKA_CLIENT_ID`             | Kafka client ID                | `iam-service`    |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTel OTLP 端点（可选）         | —                |
| `LOG_LEVEL`                   | 日志级别                       | `info`           |
| `NODE_ENV`                    | 环境标识                       | `development`    |

## REST API

| Method | Path                            | 权限              | 说明                               |
| ------ | ------------------------------- | ----------------- | ---------------------------------- |
| POST   | `/v1/iam/register`              | 公开              | 注册                               |
| POST   | `/v1/iam/login`                 | 公开              | 登录                               |
| POST   | `/v1/iam/refresh`               | 公开              | 刷新令牌（轮换 + 旧 jti 加黑名单） |
| POST   | `/v1/iam/logout`                | `IAM_USER_READ`   | 登出                               |
| GET    | `/v1/iam/me`                    | `IAM_USER_READ`   | 当前用户信息                       |
| GET    | `/v1/iam/viewports`             | `IAM_USER_READ`   | 当前用户视口                       |
| GET    | `/v1/iam/permissions/effective` | `IAM_USER_READ`   | 当前用户有效权限                   |
| GET    | `/v1/iam/children`              | `IAM_USER_READ`   | 家长的孩子列表                     |
| GET    | `/v1/iam/roles`                 | `IAM_ROLE_MANAGE` | 角色列表                           |
| GET    | `/v1/iam/permissions`           | `IAM_ROLE_MANAGE` | 权限点列表                         |
| GET    | `/v1/iam/audit`                 | `IAM_AUDIT_READ`  | 审计日志                           |
| GET    | `/v1/iam/.well-known/jwks.json` | 公开              | RS256 公钥 JWK Set                 |

## gRPC API

12 RPC，proto 定义见 `packages/shared-proto/proto/iam.proto`，package `next_edu_cloud.iam.v1`：

`Register` / `Login` / `RefreshToken` / `Logout` / `GetUserInfo` / `BatchGetUsers` / `GetEffectivePermissions` / `GetEffectiveAccess` / `GetEffectiveDataScope` / `GetViewports` / `GetPublicKey` / `GetChildrenByParent`

## 健康检查

| 端点           | 用途                                                                      | 鉴权 |
| -------------- | ------------------------------------------------------------------------- | ---- |
| `GET /healthz` | 存活探针（liveness），仅返回进程状态                                      | 无   |
| `GET /readyz`  | 就绪探针（readiness），检查 DB/Redis/Kafka/JWKS/gRPC 5 依赖，失败返回 503 | 无   |

## 数据表（10 张）

| 表名                    | 用途                        |
| ----------------------- | --------------------------- |
| `iam_users`             | 用户主表                    |
| `iam_roles`             | 角色表（role_type + level） |
| `iam_user_roles`        | 用户-角色绑定               |
| `iam_permissions`       | 权限点表                    |
| `iam_role_permissions`  | 角色-权限映射               |
| `iam_refresh_tokens`    | refresh token（含 jti）     |
| `iam_role_viewports`    | 角色-视口配置（含 level）   |
| `iam_student_guardians` | 学生-家长关系               |
| `iam_user_audit_log`    | 审计日志                    |
| `iam_password_history`  | 密码重用限制                |

DDL + 种子数据见 `scripts/iam-init.sql`。

## Kafka 事件

| Topic                   | 事件           |
| ----------------------- | -------------- |
| `edu.iam.user.events`   | `UserCreated`  |
| `edu.iam.role.events`   | `RoleChanged`  |
| `edu.iam.audit.created` | `AuditCreated` |

通过 shared-ts `OutboxModule` 事务性发布（at-least-once + idempotent producer）。

## 开发

```bash
# 安装依赖
pnpm install

# 初始化数据库（需先启动 MySQL + Redis + Kafka）
mysql -u root -p < scripts/iam-init.sql

# 生成 RS256 密钥对
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem

# 启动开发服务
pnpm --filter @edu/iam-service dev

# 质量校验
pnpm --filter @edu/iam-service lint
pnpm --filter @edu/iam-service typecheck
```
