@zskj-idaas/sdk
v2.3.0
Published
IDaaS OIDC Client SDK for browser applications — supports Authorization Code Flow with PKCE, token management, and authenticated fetch.
Maintainers
Readme
@zskj-idaas/sdk
IDaaS OIDC Client SDK — 适用于浏览器应用的 OpenID Connect 客户端,支持 Authorization Code Flow with PKCE。
当前生产环境的 OIDC Issuer 是 https://account.genisomai.com。本文中的 IDaaS 端点均填写为完整生产地址。
特性
- 零运行时依赖 — 轻量、无外部依赖
- PKCE 支持 — 自动生成 code_verifier / code_challenge
- 双模块输出 — 同时支持 ESM 和 CommonJS
- TypeScript — 完整的类型定义
- OIDC Discovery — 自动发现 OpenID 配置端点
- Token 管理 — 自动存储、过期检测、静默刷新
- 认证 Fetch — 内置带 Bearer Token 的 fetch 封装
安装
npm install @zskj-idaas/sdk
# 或
pnpm add @zskj-idaas/sdk
# 或
yarn add @zskj-idaas/sdk快速开始
import { IdaasClient } from '@zskj-idaas/sdk';
const client = new IdaasClient({
issuer: 'https://account.genisomai.com',
clientId: 'my-app',
redirectUri: `${window.location.origin}/callback`,
postLogoutRedirectUri: window.location.origin,
scope: 'openid profile email',
});
// 初始化(自动发现 OIDC 端点)
await client.init();
// 发起登录
await client.login();clientId 必须替换为 IDaaS 管理员分配的 Public Client ID。当前 Origin、${window.location.origin}/callback 和 window.location.origin 必须分别登记到管理后台的 CORS Origins、Redirect URIs 和 Post Logout Redirect URIs。
生产端点
SDK 默认从 Discovery 自动读取标准 OIDC 端点。当前生产环境的完整地址如下:
| 用途 | 方法 | 完整地址 |
| --- | --- | --- |
| OIDC Discovery | GET | https://account.genisomai.com/.well-known/openid-configuration |
| JWKS 公钥 | GET | https://account.genisomai.com/.well-known/jwks.json |
| Authorization | GET | https://account.genisomai.com/oauth2/authorize |
| Token | POST | https://account.genisomai.com/oauth2/token |
| UserInfo | GET / POST | https://account.genisomai.com/oauth2/userinfo |
| 企业资料(当前用户) | GET | https://account.genisomai.com/oauth2/enterprise-profile |
| Business Token | POST | https://account.genisomai.com/oauth2/business-token |
| Logout | GET | https://account.genisomai.com/oauth2/logout |
| Revoke | POST | https://account.genisomai.com/oauth2/revoke |
| Introspect | POST | https://account.genisomai.com/oauth2/introspect |
| 清理 IDaaS Session | POST | https://account.genisomai.com/auth/logout |
| Service Token Verify(仅业务后端) | POST | https://account.genisomai.com/api/service-auth/verify |
| Authorization Evaluate(仅业务后端,以 my-app-service 为例) | POST | https://account.genisomai.com/api/service-auth/my-app-service/evaluate |
浏览器不能持有 ServiceCredential,因此不能直接调用最后两个服务端授权端点。my-app-service 是 Service Key 示例,实际接入时使用管理员分配的值。
API 参考
new IdaasClient(config)
创建 SDK 客户端实例。
interface IdaasConfig {
issuer: string; // OIDC 提供者地址
clientId: string; // 客户端 ID
redirectUri: string; // 登录回调地址
postLogoutRedirectUri?: string; // 登出后跳转地址
scope?: string; // 请求的 scope(默认: 'openid profile email')
autoDiscovery?: boolean; // 是否自动发现端点(默认: true)
storagePrefix?: string; // 存储 key 前缀(默认: 'idaas')
}该 SDK 仅用于浏览器 Public Client,始终使用 PKCE,不能配置或传输 Client Secret。
client.init(): Promise<AuthState>
初始化客户端,执行 OIDC Discovery 并返回当前认证状态。
client.login(extraParams?): Promise<void>
发起 OIDC 授权码登录流程,跳转到授权端点。
// 附加额外参数
await client.login({ prompt: 'login', login_hint: '[email protected]' });client.handleCallback(callbackUrl?): Promise<TokenSet>
处理授权回调,用授权码交换 Token。
// 在回调页面调用
const tokens = await client.handleCallback();client.getUserInfo(forceRefresh?): Promise<UserInfo | null>
获取当前用户信息。
管理员为 Client 开放 feishu_identity,并在登录时申请且经用户同意后,可返回以下可选字段:
feishu_union_id与feishu_tenant_key:必须成对完整,供同企业、同应用开发商范围内的跨飞书应用身份关联。feishu_open_id:同一身份元数据中合法的 Open ID,供 IDaaS 所用飞书应用内关联,可与primary_department_leaders[].feishu_open_id对应。
未授权、身份歧义或 UnionID/企业标识缺失时不返回上述字段;仅 Open ID 缺失或非法时省略 feishu_open_id,保留原有完整关联键。不得从用户名推导 Open ID,也不得用它跨飞书应用匹配。身份字段不授予业务权限,不写入 ID Token 或业务 Token。
const user = await client.getUserInfo(true); // 强制重新获取,避免沿用先前缓存
const feishuOpenId = user?.feishu_open_id;
// 使用 feishuOpenId 进行同一飞书应用内关联;缺失时跳过关联,不记录身份值到日志。client.getEnterpriseProfile(): Promise<EnterpriseProfile | null>
按当前应用获准且用户已同意的 Scope 获取当前用户的敏感企业资料。支持的独立 Scope 为 phone、employee_number 和 primary_department;对应字段不会进入 ID Token。
import { EnterpriseProfileError } from '@zskj-idaas/sdk';
try {
const profile = await client.getEnterpriseProfile();
console.log(profile?.employee_number);
console.log(profile?.primary_department_path_name);
console.log(profile?.primary_department_leaders);
} catch (error) {
if (error instanceof EnterpriseProfileError && error.retryable) {
// 提示用户稍后重试,不要把查询失败当成字段为空。
}
}primary_department_path 按一级部门到叶子部门排序,每项包含 id 和 name,不包含公司根节点;primary_department_path_name 是使用 / 连接的可展示路径。primary_department_leaders 返回主部门的主负责人和副负责人,每项包含 feishu_open_id、name、avatar_url 和 leader_type;leader_type 取值为 primary 或 deputy。负责人仍只需 primary_department scope,读取本人 feishu_open_id 则需 feishu_identity。兼容旧服务时,调用方可回退到 primary_department_name。
2.3.0 兼容升级
本版本为 EnterpriseDepartmentLeader 新增 feishu_open_id,并保留 open_id 等值兼容别名(标记为废弃);新服务端同时返回两个字段。为兼容旧服务和 2.x 调用方创建的对象,SDK 将新增字段声明为可选,可使用 leader.feishu_open_id ?? leader.open_id。UserInfo 新增可选的 feishu_open_id、feishu_union_id 和 feishu_tenant_key 类型。SDK 版本更新不等于服务端已部署或 npm 已发布。
client.getAccessToken(): Promise<string | null>
获取有效的 Access Token(过期时自动刷新)。
client.refreshToken(): Promise<TokenSet | null>
使用 Refresh Token 主动刷新 Token;刷新失败时清理本地登录态并返回 null。
client.getServiceToken(serviceKey): Promise<string | null>
调用 https://account.genisomai.com/oauth2/business-token 获取指定 Service 的业务 Token,并按 serviceKey 隔离缓存。
client.getIdToken(): string | null
获取 ID Token。
client.getAuthState(): AuthState
获取当前认证状态。
interface AuthState {
isAuthenticated: boolean;
user: UserInfo | null;
accessToken: string | null;
idToken: string | null;
expiresAt: number | null;
}client.isAuthenticated(): boolean
检查是否已认证。
client.logout(): Promise<void>
清理本地 Token,并跳转到 https://account.genisomai.com/oauth2/logout 执行 OIDC 登出。
client.logoutSession(options?): Promise<void>
清理本地 Token,并调用 https://account.genisomai.com/auth/logout 销毁 IDaaS Session;可通过 redirectTo 指定完成后的前端地址。
client.revokeToken(): Promise<void>
撤销 Refresh Token。
client.fetch(url, init?): Promise<Response>
带基础 IDaaS Access Token 的 fetch 封装,自动附加 Authorization 头。基础 Token 只允许用于 IDaaS UserInfo 和 Business Token 端点,不能用于业务 API;读取用户信息时优先使用 getUserInfo()。
const res = await client.fetch('https://account.genisomai.com/oauth2/userinfo');
const data = await res.json();client.fetchService(serviceKey, url, init?): Promise<Response>
获取指定 Service 的业务 Token,并以 Bearer Token 调用业务 API。
const res = await client.fetchService('my-app-service', '/api/customers');
const data = await res.json();完整示例
import { IdaasClient } from '@zskj-idaas/sdk';
import type { AuthState, UserInfo } from '@zskj-idaas/sdk';
const client = new IdaasClient({
issuer: 'https://account.genisomai.com',
clientId: 'my-app',
redirectUri: `${window.location.origin}/callback`,
postLogoutRedirectUri: window.location.origin,
});
// 应用启动时初始化
async function bootstrap() {
const state: AuthState = await client.init();
if (state.isAuthenticated) {
const user: UserInfo | null = await client.getUserInfo();
console.log('已登录:', user?.name);
}
}
// 回调页面处理
async function handleCallback() {
try {
await client.handleCallback();
window.location.href = '/';
} catch (err) {
console.error('登录失败:', err);
}
}
// 调用受保护的 API
async function fetchData() {
const res = await client.fetchService('my-app-service', '/api/data');
return res.json();
}导出内容
// 类
export { EnterpriseProfileError, IdaasClient } from './client';
export { TokenStorage } from './storage';
// 工具函数
export { createPKCE, generateState, generateNonce } from './crypto';
// 类型
export type { IdaasConfig, TokenSet, UserInfo, EnterpriseDepartmentPathItem, EnterpriseProfile, AuthState } from './types';浏览器兼容性
需要支持 Web Crypto API 的现代浏览器:
- Chrome 63+
- Firefox 57+
- Safari 11.1+
- Edge 79+
License
MIT
