# 微服务接入指南

本服务是 OIDC 认证授权中台，为其它微服务提供：用户认证（JWT/RS256）、权限校验（RBAC）、服务间调用（API Key）。

- **签发方（issuer）**：`https://auth.ai-as.cc`
- **签名算法**：RS256（非对称）
- **发现端点**：`https://auth.ai-as.cc/.well-known/openid-configuration`
- **JWKS**：`https://auth.ai-as.cc/.well-known/jwks.json`

业务系统接入有两条路径，按需选择。

---

## 一、认证：验证用户 Token

用户登录本服务后获得 JWT（RS256 签名）。业务系统有两种验证方式：

### 方式 A：JWKS 本地验签（推荐）

业务系统从 JWKS 端点获取公钥，**本地**验证签名。无需共享密钥，auth 服务挂了也不影响已签发 token 的验证。

**关键点**：JWT header 带 `kid`，用于在 JWKS 中定位对应公钥（轮换密钥时会有多个 kid）。

#### Rust（jsonwebtoken）

```rust
use jsonwebtoken::{decode, Validation, DecodingKey, Algorithm};
use jsonwebtoken::jwk::JwkSet;

// 1. 拉取 JWKS（建议缓存 + 定期刷新，如每 10 分钟）
let jwks: JwkSet = reqwest::get("https://auth.ai-as.cc/.well-known/jwks.json")
    .await?.json().await?;

// 2. 解析 token header 拿 kid
let header = jsonwebtoken::decode_header(&token)?;
let kid = header.kid.ok_or("missing kid")?;
let jwk = jwks.find(&kid).ok_or("kid not found")?;
let decoding_key = DecodingKey::from_jwk(jwk)?;

// 3. 验签 + 校验 iss + exp
let mut validation = Validation::new(Algorithm::RS256);
validation.set_issuer(&["https://auth.ai-as.cc"]);
let token_data = decode::<MyClaims>(&token, &decoding_key, &validation)?;
// token_data.claims.sub = 用户名，claims.permissions = 权限列表
```

#### Node.js（jose）

```js
import { createRemoteJWKSet, jwtVerify } from 'jose';

const JWKS = createRemoteJWKSet(
  new URL('https://auth.ai-as.cc/.well-known/jwks.json')
);

const { payload } = await jwtVerify(token, JWKS, {
  issuer: 'https://auth.ai-as.cc',
  algorithms: ['RS256'],
});
// payload.sub = 用户名，payload.permissions = 权限列表
```

### 方式 B：内省接口（POST /api/auth/verify）

业务系统把 token 发给 auth 验证，返回用户信息。简单，但每次都依赖 auth 服务在线。

```bash
curl -X POST https://auth.ai-as.cc/api/auth/verify \
  -H 'Content-Type: application/json' \
  -d '{"token": "<用户的JWT>"}'
```

返回：
```json
{
  "success": true,
  "data": {
    "user_id": 1,
    "username": "admin",
    "permissions": ["system:user:list", ...],
    "expires_at": 1781908274,
    "tenant_id": "admin"
  }
}
```

**选哪种？** 高频鉴权用方式 A（本地验签，零延迟、不依赖 auth）；低频或需要实时权限用方式 B（内省能拿到最新权限，因为 auth 验证时会从 DB 刷新 permissions）。

---

## 二、授权：RBAC 权限校验

Token 的 `permissions` 字段是用户拥有的按钮级权限（如 `publish:write`）。业务系统据此决定能否执行操作。

### 本地校验（推荐，配合方式 A）

token 验签后直接读 `permissions` 数组判断：

```rust
if !token_data.claims.permissions.contains(&"publish:write".to_string()) {
    return Err(Forbidden);
}
```

> 注意：token 里的 permissions 是签发时的快照。若需**实时**权限（用户角色刚被改），用下面的远程校验。

### 远程校验（POST /api/permissions/check）

```bash
curl -X POST https://auth.ai-as.cc/api/permissions/check \
  -H 'Content-Type: application/json' \
  -d '{"user_id": 1, "permission": "publish:write"}'
```

返回 `{"success": true, "data": {"has_permission": true}}`。

---

## 三、服务间调用：API Key

微服务之间互调用 API Key 鉴权（不涉及用户）。在管理后台创建 Key 时选择该服务拥有的权限范围。

```bash
curl -X POST https://auth.ai-as.cc/api/keys/validate \
  -H 'Content-Type: application/json' \
  -d '{"api_key": "<你的API Key>", "required_permissions": ["publish:write"]}'
```

返回 `{"success": true, "data": {"valid": true, "api_key_info": {...}}}`。

带 account 维度（多租户）用 `/api/keys/validate-for-account`。

---

## 四、OIDC Claims 字段说明

| 字段 | 含义 |
|------|------|
| `iss` | 签发方（`https://auth.ai-as.cc`） |
| `sub` | 用户名 |
| `user_id` | 用户 ID（数字） |
| `jti` | token 唯一 ID（用于 session 管理） |
| `exp` / `iat` | 过期 / 签发时间（Unix 时间戳） |
| `permissions` | 按钮级权限标识列表 |
| `tenant_id` | 租户标识（默认 = 用户名） |

access token 默认有效期 24 小时；refresh token 7 天。

---

## 五、密钥轮换

签发密钥是 RSA 文件（`keys/private.pem` + `keys/public.pem`）。

轮换流程：
1. 生成新密钥对替换文件
2. 重启服务
3. 旧 token 在过期前仍可被旧公钥验证（若需要平滑轮换，需在 JWKS 暴露多 kid——当前版本为单密钥，轮换后旧 token 直接失效，用户重新登录）

---

## 六、快速自检

接入后验证：
```bash
# 发现端点可访问
curl https://auth.ai-as.cc/.well-known/openid-configuration | jq .issuer

# JWKS 可访问
curl https://auth.ai-as.cc/.well-known/jwks.json | jq '.keys[0].kid'

# 用你的语言库 + JWKS 公钥验签一个 token
```
