# UserMatrix (UM) SDK

OAuth 2.1 + OIDC 标准对接 SDK，一行代码接入云集统一身份服务。

## 官方统一标准

云集生态的身份对接统一收敛于 **OAuth 2.1 + OIDC** 标准：

| 端点 | 路径 | 说明 |
| --- | --- | --- |
| 授权 | `GET /oauth/authorize.php` | Authorization Code 端点，支持 PKCE（S256） |
| 令牌 | `POST /oauth/token.php` | 支持 `authorization_code` / `refresh_token` / `client_credentials` |
| 用户信息 | `GET /oauth/userinfo.php` | OIDC UserInfo，返回标准 claims |
| 撤销 | `POST /oauth/revoke.php` | RFC 7009 令牌撤销 |
| 发现 | `GET /.well-known/openid-configuration` | OIDC Discovery，兼容通用 OAuth 库 |

API 根地址：`https://um.yunjii.cn/`

## 包含文件

| 文件 | 用途 | 适用场景 |
| --- | --- | --- |
| `UM.class.php` | PHP SDK | 后端 OAuth 2.1 + PKCE 全流程 |
| `um-sdk.js` | JS SDK | 前端嵌入二维码 / SPA 跳转登录 |
| `example.php` | PHP 完整示例 | OAuth 2.1 + PKCE 参考实现 |
| `example-js.html` | JS 完整示例 | PKCE 跳转 + 扫码两种模式 |

## 5 分钟接入

### 1. 创建应用

访问 https://um.yunjii.cn/console/apps 创建应用，记录 `appid` 和 `appkey`，并配置回调域名。

### 2. PHP 后端接入（OAuth 2.1 + PKCE）

```php
require_once 'UM.class.php';

$um = new UM(
    '你的appid',
    '你的appkey',
    'https://你的域名/callback.php'
);

// ── 登录入口：生成授权 URL ──
session_start();
$state    = bin2hex(random_bytes(16));
$verifier = $um->generateCodeVerifier();           // PKCE
$_SESSION['oauth_state']    = $state;
$_SESSION['oauth_verifier'] = $verifier;

header('Location: ' . $um->authorizeUrl($state, $verifier));

// ── 回调页：code + code_verifier 换 token ──
$tokenRes = $um->token($_GET['code'], $_SESSION['oauth_verifier']);
$user     = $um->userinfo($tokenRes['access_token']);
// => ['sub'=>'um_xxx', 'nickname'=>'张三', 'picture'=>'...', 'login_type'=>'wx', ...]
```

### 3. 前端接入（JS SDK + PKCE）

```html
<script src="https://um.yunjii.cn/sdk/um-sdk.js"></script>
<script>
UM.config.appid = '你的appid';
UM.config.appkey = '你的appkey';
UM.config.redirect_uri = location.origin + '/callback.html';

// OAuth 2.1 + PKCE 跳转
async function login() {
    const verifier = UM.generateCodeVerifier();
    const challenge = await UM.generateCodeChallenge(verifier);
    sessionStorage.setItem('um_pkce', verifier);
    location.href = UM.authorizeUrl('state_xyz', challenge);
}

// 回调页：换 token + 用户信息
async function callback() {
    const code = new URLSearchParams(location.search).get('code');
    const verifier = sessionStorage.getItem('um_pkce');
    const token = await UM.token(code, verifier);
    const user = await UM.userinfo(token.access_token);
    console.log(user);
}
</script>
```

### 4. 扫码登录（二维码图片，适合 PC 端）

```html
<div id="um-login"></div>
<script src="https://um.yunjii.cn/sdk/um-sdk.js"></script>
<script>
UM.init({
    mode: 'qrcode',        // ★ 二维码图片（无 iframe、无跨域限制）
    appid: '你的appid',
    appkey: '你的appkey',
    element: '#um-login',
    type: 'wx',
    redirect_uri: 'https://你的域名/callback.html',
    onLogin: function(res) { location.href = '/home'; }
});
</script>
```

## SDK API 速查

### PHP SDK

| 方法 | 说明 |
| --- | --- |
| `generateCodeVerifier()` | 生成 PKCE code_verifier |
| `generateCodeChallenge($verifier)` | 计算 code_challenge（S256） |
| `authorizeUrl($state, $verifier, $scope)` | 生成授权 URL（自动加 PKCE） |
| `token($code, $verifier)` | code 换 access_token |
| `refreshToken($refreshToken)` | 刷新 token（轮换） |
| `userinfo($accessToken)` | 获取 OIDC 用户信息 |
| `revoke($token, $hint)` | 撤销令牌 |
| `clientCredentials($scope)` | 机器到机器模式 |
| `qrcodeUrl($type, $size, $state)` | 二维码图片 URL |

### JS SDK

| 方法 | 说明 |
| --- | --- |
| `UM.generateCodeVerifier()` | 生成 PKCE code_verifier（sync） |
| `UM.generateCodeChallenge(verifier)` | 计算 code_challenge（async，Web Crypto） |
| `UM.authorizeUrl(state, challenge, scope)` | 生成授权 URL |
| `UM.token(code, verifier)` | code 换 token（Promise） |
| `UM.refreshToken(refreshToken)` | 刷新 token |
| `UM.userinfo(accessToken)` | 获取用户信息 |
| `UM.revoke(token, hint)` | 撤销令牌 |
| `UM.init({mode:'qrcode', ...})` | 二维码 / 跳转 / 弹窗模式 |

## 兼容性

- 与旧版彩虹聚合登录协议（`connect.php?act=*`）向后兼容，老应用零改动
- 旧方法 `$um->login()` / `$um->callback()` / `$um->query()` 标注 `@deprecated`，仍可用
- Nginx 兼容路由 `/um/*` → `/php/*`，老 SDK 调用不受影响

## 完整 API 文档

访问 https://um.yunjii.cn/zh/docs/api/oauth 查看完整接口文档。
