@zskj-idaas/sdk 1.0.4 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -12
- package/dist/index.d.mts +57 -3
- package/dist/index.d.ts +57 -3
- package/dist/index.js +35 -6
- package/dist/index.mjs +34 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
IDaaS OIDC Client SDK — 适用于浏览器应用的 OpenID Connect 客户端,支持 Authorization Code Flow with PKCE。
|
|
4
4
|
|
|
5
|
+
当前生产环境的 OIDC Issuer 是 `https://account.genisomai.com`。本文中的 IDaaS 端点均填写为完整生产地址。
|
|
6
|
+
|
|
5
7
|
## 特性
|
|
6
8
|
|
|
7
9
|
- **零运行时依赖** — 轻量、无外部依赖
|
|
@@ -28,9 +30,10 @@ yarn add @zskj-idaas/sdk
|
|
|
28
30
|
import { IdaasClient } from '@zskj-idaas/sdk';
|
|
29
31
|
|
|
30
32
|
const client = new IdaasClient({
|
|
31
|
-
issuer: 'https://
|
|
32
|
-
clientId: '
|
|
33
|
-
redirectUri:
|
|
33
|
+
issuer: 'https://account.genisomai.com',
|
|
34
|
+
clientId: 'my-app',
|
|
35
|
+
redirectUri: `${window.location.origin}/callback`,
|
|
36
|
+
postLogoutRedirectUri: window.location.origin,
|
|
34
37
|
scope: 'openid profile email',
|
|
35
38
|
});
|
|
36
39
|
|
|
@@ -41,6 +44,30 @@ await client.init();
|
|
|
41
44
|
await client.login();
|
|
42
45
|
```
|
|
43
46
|
|
|
47
|
+
`clientId` 必须替换为 IDaaS 管理员分配的 Public Client ID。当前 Origin、`${window.location.origin}/callback` 和 `window.location.origin` 必须分别登记到管理后台的 CORS Origins、Redirect URIs 和 Post Logout Redirect URIs。
|
|
48
|
+
|
|
49
|
+
## 生产端点
|
|
50
|
+
|
|
51
|
+
SDK 默认从 Discovery 自动读取标准 OIDC 端点。当前生产环境的完整地址如下:
|
|
52
|
+
|
|
53
|
+
| 用途 | 方法 | 完整地址 |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| OIDC Discovery | GET | `https://account.genisomai.com/.well-known/openid-configuration` |
|
|
56
|
+
| JWKS 公钥 | GET | `https://account.genisomai.com/.well-known/jwks.json` |
|
|
57
|
+
| Authorization | GET | `https://account.genisomai.com/oauth2/authorize` |
|
|
58
|
+
| Token | POST | `https://account.genisomai.com/oauth2/token` |
|
|
59
|
+
| UserInfo | GET / POST | `https://account.genisomai.com/oauth2/userinfo` |
|
|
60
|
+
| 企业资料(当前用户) | GET | `https://account.genisomai.com/oauth2/enterprise-profile` |
|
|
61
|
+
| Business Token | POST | `https://account.genisomai.com/oauth2/business-token` |
|
|
62
|
+
| Logout | GET | `https://account.genisomai.com/oauth2/logout` |
|
|
63
|
+
| Revoke | POST | `https://account.genisomai.com/oauth2/revoke` |
|
|
64
|
+
| Introspect | POST | `https://account.genisomai.com/oauth2/introspect` |
|
|
65
|
+
| 清理 IDaaS Session | POST | `https://account.genisomai.com/auth/logout` |
|
|
66
|
+
| Service Token Verify(仅业务后端) | POST | `https://account.genisomai.com/api/service-auth/verify` |
|
|
67
|
+
| Authorization Evaluate(仅业务后端,以 `my-app-service` 为例) | POST | `https://account.genisomai.com/api/service-auth/my-app-service/evaluate` |
|
|
68
|
+
|
|
69
|
+
浏览器不能持有 ServiceCredential,因此不能直接调用最后两个服务端授权端点。`my-app-service` 是 Service Key 示例,实际接入时使用管理员分配的值。
|
|
70
|
+
|
|
44
71
|
## API 参考
|
|
45
72
|
|
|
46
73
|
### `new IdaasClient(config)`
|
|
@@ -51,7 +78,6 @@ await client.login();
|
|
|
51
78
|
interface IdaasConfig {
|
|
52
79
|
issuer: string; // OIDC 提供者地址
|
|
53
80
|
clientId: string; // 客户端 ID
|
|
54
|
-
clientSecret?: string; // 客户端密钥(机密客户端)
|
|
55
81
|
redirectUri: string; // 登录回调地址
|
|
56
82
|
postLogoutRedirectUri?: string; // 登出后跳转地址
|
|
57
83
|
scope?: string; // 请求的 scope(默认: 'openid profile email')
|
|
@@ -60,6 +86,8 @@ interface IdaasConfig {
|
|
|
60
86
|
}
|
|
61
87
|
```
|
|
62
88
|
|
|
89
|
+
该 SDK 仅用于浏览器 Public Client,始终使用 PKCE,不能配置或传输 Client Secret。
|
|
90
|
+
|
|
63
91
|
### `client.init(): Promise<AuthState>`
|
|
64
92
|
|
|
65
93
|
初始化客户端,执行 OIDC Discovery 并返回当前认证状态。
|
|
@@ -70,7 +98,7 @@ interface IdaasConfig {
|
|
|
70
98
|
|
|
71
99
|
```typescript
|
|
72
100
|
// 附加额外参数
|
|
73
|
-
await client.login({ prompt: 'login', login_hint: 'user@
|
|
101
|
+
await client.login({ prompt: 'login', login_hint: 'user@company.test' });
|
|
74
102
|
```
|
|
75
103
|
|
|
76
104
|
### `client.handleCallback(callbackUrl?): Promise<TokenSet>`
|
|
@@ -86,10 +114,56 @@ const tokens = await client.handleCallback();
|
|
|
86
114
|
|
|
87
115
|
获取当前用户信息。
|
|
88
116
|
|
|
117
|
+
管理员为 Client 开放 `feishu_identity`,并在登录时申请且经用户同意后,可返回以下可选字段:
|
|
118
|
+
|
|
119
|
+
- `feishu_union_id` 与 `feishu_tenant_key`:必须成对完整,供同企业、同应用开发商范围内的跨飞书应用身份关联。
|
|
120
|
+
- `feishu_open_id`:同一身份元数据中合法的 Open ID,供 IDaaS 所用飞书应用内关联,可与 `primary_department_leaders[].feishu_open_id` 对应。
|
|
121
|
+
|
|
122
|
+
未授权、身份歧义或 UnionID/企业标识缺失时不返回上述字段;仅 Open ID 缺失或非法时省略 `feishu_open_id`,保留原有完整关联键。不得从用户名推导 Open ID,也不得用它跨飞书应用匹配。身份字段不授予业务权限,不写入 ID Token 或业务 Token。
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const user = await client.getUserInfo(true); // 强制重新获取,避免沿用先前缓存
|
|
126
|
+
const feishuOpenId = user?.feishu_open_id;
|
|
127
|
+
// 使用 feishuOpenId 进行同一飞书应用内关联;缺失时跳过关联,不记录身份值到日志。
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `client.getEnterpriseProfile(): Promise<EnterpriseProfile | null>`
|
|
131
|
+
|
|
132
|
+
按当前应用获准且用户已同意的 Scope 获取当前用户的敏感企业资料。支持的独立 Scope 为 `phone`、`employee_number` 和 `primary_department`;对应字段不会进入 ID Token。
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { EnterpriseProfileError } from '@zskj-idaas/sdk';
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const profile = await client.getEnterpriseProfile();
|
|
139
|
+
console.log(profile?.employee_number);
|
|
140
|
+
console.log(profile?.primary_department_path_name);
|
|
141
|
+
console.log(profile?.primary_department_leaders);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
if (error instanceof EnterpriseProfileError && error.retryable) {
|
|
144
|
+
// 提示用户稍后重试,不要把查询失败当成字段为空。
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`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`。
|
|
150
|
+
|
|
151
|
+
### 2.3.0 兼容升级
|
|
152
|
+
|
|
153
|
+
本版本为 `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 已发布。
|
|
154
|
+
|
|
89
155
|
### `client.getAccessToken(): Promise<string | null>`
|
|
90
156
|
|
|
91
157
|
获取有效的 Access Token(过期时自动刷新)。
|
|
92
158
|
|
|
159
|
+
### `client.refreshToken(): Promise<TokenSet | null>`
|
|
160
|
+
|
|
161
|
+
使用 Refresh Token 主动刷新 Token;刷新失败时清理本地登录态并返回 `null`。
|
|
162
|
+
|
|
163
|
+
### `client.getServiceToken(serviceKey): Promise<string | null>`
|
|
164
|
+
|
|
165
|
+
调用 `https://account.genisomai.com/oauth2/business-token` 获取指定 Service 的业务 Token,并按 `serviceKey` 隔离缓存。
|
|
166
|
+
|
|
93
167
|
### `client.getIdToken(): string | null`
|
|
94
168
|
|
|
95
169
|
获取 ID Token。
|
|
@@ -114,7 +188,11 @@ interface AuthState {
|
|
|
114
188
|
|
|
115
189
|
### `client.logout(): Promise<void>`
|
|
116
190
|
|
|
117
|
-
执行 OIDC
|
|
191
|
+
清理本地 Token,并跳转到 `https://account.genisomai.com/oauth2/logout` 执行 OIDC 登出。
|
|
192
|
+
|
|
193
|
+
### `client.logoutSession(options?): Promise<void>`
|
|
194
|
+
|
|
195
|
+
清理本地 Token,并调用 `https://account.genisomai.com/auth/logout` 销毁 IDaaS Session;可通过 `redirectTo` 指定完成后的前端地址。
|
|
118
196
|
|
|
119
197
|
### `client.revokeToken(): Promise<void>`
|
|
120
198
|
|
|
@@ -122,10 +200,19 @@ interface AuthState {
|
|
|
122
200
|
|
|
123
201
|
### `client.fetch(url, init?): Promise<Response>`
|
|
124
202
|
|
|
125
|
-
|
|
203
|
+
带基础 IDaaS Access Token 的 fetch 封装,自动附加 Authorization 头。基础 Token 只允许用于 IDaaS UserInfo 和 Business Token 端点,不能用于业务 API;读取用户信息时优先使用 `getUserInfo()`。
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
const res = await client.fetch('https://account.genisomai.com/oauth2/userinfo');
|
|
207
|
+
const data = await res.json();
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### `client.fetchService(serviceKey, url, init?): Promise<Response>`
|
|
211
|
+
|
|
212
|
+
获取指定 Service 的业务 Token,并以 Bearer Token 调用业务 API。
|
|
126
213
|
|
|
127
214
|
```typescript
|
|
128
|
-
const res = await client.
|
|
215
|
+
const res = await client.fetchService('my-app-service', '/api/customers');
|
|
129
216
|
const data = await res.json();
|
|
130
217
|
```
|
|
131
218
|
|
|
@@ -136,7 +223,7 @@ import { IdaasClient } from '@zskj-idaas/sdk';
|
|
|
136
223
|
import type { AuthState, UserInfo } from '@zskj-idaas/sdk';
|
|
137
224
|
|
|
138
225
|
const client = new IdaasClient({
|
|
139
|
-
issuer: 'https://
|
|
226
|
+
issuer: 'https://account.genisomai.com',
|
|
140
227
|
clientId: 'my-app',
|
|
141
228
|
redirectUri: `${window.location.origin}/callback`,
|
|
142
229
|
postLogoutRedirectUri: window.location.origin,
|
|
@@ -164,7 +251,7 @@ async function handleCallback() {
|
|
|
164
251
|
|
|
165
252
|
// 调用受保护的 API
|
|
166
253
|
async function fetchData() {
|
|
167
|
-
const res = await client.
|
|
254
|
+
const res = await client.fetchService('my-app-service', '/api/data');
|
|
168
255
|
return res.json();
|
|
169
256
|
}
|
|
170
257
|
```
|
|
@@ -173,14 +260,14 @@ async function fetchData() {
|
|
|
173
260
|
|
|
174
261
|
```typescript
|
|
175
262
|
// 类
|
|
176
|
-
export { IdaasClient } from './client';
|
|
263
|
+
export { EnterpriseProfileError, IdaasClient } from './client';
|
|
177
264
|
export { TokenStorage } from './storage';
|
|
178
265
|
|
|
179
266
|
// 工具函数
|
|
180
267
|
export { createPKCE, generateState, generateNonce } from './crypto';
|
|
181
268
|
|
|
182
269
|
// 类型
|
|
183
|
-
export type { IdaasConfig, TokenSet, UserInfo, AuthState } from './types';
|
|
270
|
+
export type { IdaasConfig, TokenSet, UserInfo, EnterpriseDepartmentPathItem, EnterpriseProfile, AuthState } from './types';
|
|
184
271
|
```
|
|
185
272
|
|
|
186
273
|
## 浏览器兼容性
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
interface IdaasConfig {
|
|
2
2
|
issuer: string;
|
|
3
3
|
clientId: string;
|
|
4
|
-
clientSecret?: string;
|
|
5
4
|
redirectUri: string;
|
|
6
5
|
postLogoutRedirectUri?: string;
|
|
7
6
|
scope?: string;
|
|
@@ -28,11 +27,51 @@ interface UserInfo {
|
|
|
28
27
|
preferred_username?: string;
|
|
29
28
|
name?: string;
|
|
30
29
|
email?: string;
|
|
31
|
-
|
|
30
|
+
email_verified?: boolean;
|
|
32
31
|
picture?: string;
|
|
32
|
+
/** Requires feishu_identity. Scoped to the Feishu app used by IDaaS; never use across Feishu apps. */
|
|
33
|
+
feishu_open_id?: string;
|
|
34
|
+
/** Requires feishu_identity. Returned together with feishu_tenant_key for cross-app identity linking. */
|
|
35
|
+
feishu_union_id?: string;
|
|
36
|
+
/** Enterprise identity namespace; only returned together with feishu_union_id. */
|
|
37
|
+
feishu_tenant_key?: string;
|
|
33
38
|
roles?: string[];
|
|
34
39
|
[key: string]: unknown;
|
|
35
40
|
}
|
|
41
|
+
/** Sensitive enterprise attributes returned only when the current client and user grant the matching scopes. */
|
|
42
|
+
interface EnterpriseDepartmentPathItem {
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
}
|
|
46
|
+
type EnterpriseDepartmentLeaderType = 'primary' | 'deputy';
|
|
47
|
+
interface EnterpriseDepartmentLeader {
|
|
48
|
+
/** Scoped to the same Feishu app as UserInfo.feishu_open_id. */
|
|
49
|
+
feishu_open_id?: string;
|
|
50
|
+
/** @deprecated Use feishu_open_id. Kept as an equal-valued alias for 2.x compatibility. */
|
|
51
|
+
open_id: string;
|
|
52
|
+
name: string | null;
|
|
53
|
+
avatar_url: string | null;
|
|
54
|
+
/** primary = 主负责人;deputy = 副负责人。 */
|
|
55
|
+
leader_type: EnterpriseDepartmentLeaderType;
|
|
56
|
+
}
|
|
57
|
+
interface EnterpriseProfile {
|
|
58
|
+
phone_number?: string | null;
|
|
59
|
+
employee_number?: string | null;
|
|
60
|
+
primary_department_id?: string | null;
|
|
61
|
+
primary_department_name?: string | null;
|
|
62
|
+
/** Ordered from the top-level department to the user's primary (leaf) department. */
|
|
63
|
+
primary_department_path?: EnterpriseDepartmentPathItem[] | null;
|
|
64
|
+
/** Human-readable path such as "一级部门 / 二级部门 / 叶子部门". */
|
|
65
|
+
primary_department_path_name?: string | null;
|
|
66
|
+
/** Main and deputy leaders of the primary department. */
|
|
67
|
+
primary_department_leaders?: EnterpriseDepartmentLeader[] | null;
|
|
68
|
+
}
|
|
69
|
+
interface EnterpriseProfileErrorBody {
|
|
70
|
+
statusCode?: number;
|
|
71
|
+
code?: string;
|
|
72
|
+
message?: string | string[];
|
|
73
|
+
retryable?: boolean;
|
|
74
|
+
}
|
|
36
75
|
interface AuthState {
|
|
37
76
|
isAuthenticated: boolean;
|
|
38
77
|
user: UserInfo | null;
|
|
@@ -49,6 +88,16 @@ interface PKCEPair {
|
|
|
49
88
|
codeChallenge: string;
|
|
50
89
|
}
|
|
51
90
|
|
|
91
|
+
declare class EnterpriseProfileError extends Error {
|
|
92
|
+
readonly status: number;
|
|
93
|
+
readonly code?: string;
|
|
94
|
+
readonly retryable: boolean;
|
|
95
|
+
constructor(message: string, options: {
|
|
96
|
+
status: number;
|
|
97
|
+
code?: string;
|
|
98
|
+
retryable?: boolean;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
52
101
|
declare class IdaasClient {
|
|
53
102
|
private config;
|
|
54
103
|
private endpoints;
|
|
@@ -62,6 +111,11 @@ declare class IdaasClient {
|
|
|
62
111
|
handleCallback(callbackUrl?: string): Promise<TokenSet>;
|
|
63
112
|
refreshToken(): Promise<TokenSet | null>;
|
|
64
113
|
getUserInfo(forceRefresh?: boolean): Promise<UserInfo | null>;
|
|
114
|
+
/**
|
|
115
|
+
* Fetch sensitive attributes for the current user. The server enforces both
|
|
116
|
+
* the client's scope allowlist and the user's consent before returning fields.
|
|
117
|
+
*/
|
|
118
|
+
getEnterpriseProfile(): Promise<EnterpriseProfile | null>;
|
|
65
119
|
getAccessToken(): Promise<string | null>;
|
|
66
120
|
getServiceToken(serviceKey: string): Promise<string | null>;
|
|
67
121
|
getIdToken(): string | null;
|
|
@@ -100,4 +154,4 @@ declare class TokenStorage {
|
|
|
100
154
|
clearAuthState(): void;
|
|
101
155
|
}
|
|
102
156
|
|
|
103
|
-
export { type AuthState, IdaasClient, type IdaasConfig, type ServiceTokenState, type SessionLogoutOptions, type TokenSet, TokenStorage, type UserInfo, createPKCE, generateNonce, generateState };
|
|
157
|
+
export { type AuthState, type EnterpriseDepartmentLeader, type EnterpriseDepartmentLeaderType, type EnterpriseDepartmentPathItem, type EnterpriseProfile, EnterpriseProfileError, type EnterpriseProfileErrorBody, IdaasClient, type IdaasConfig, type ServiceTokenState, type SessionLogoutOptions, type TokenSet, TokenStorage, type UserInfo, createPKCE, generateNonce, generateState };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
interface IdaasConfig {
|
|
2
2
|
issuer: string;
|
|
3
3
|
clientId: string;
|
|
4
|
-
clientSecret?: string;
|
|
5
4
|
redirectUri: string;
|
|
6
5
|
postLogoutRedirectUri?: string;
|
|
7
6
|
scope?: string;
|
|
@@ -28,11 +27,51 @@ interface UserInfo {
|
|
|
28
27
|
preferred_username?: string;
|
|
29
28
|
name?: string;
|
|
30
29
|
email?: string;
|
|
31
|
-
|
|
30
|
+
email_verified?: boolean;
|
|
32
31
|
picture?: string;
|
|
32
|
+
/** Requires feishu_identity. Scoped to the Feishu app used by IDaaS; never use across Feishu apps. */
|
|
33
|
+
feishu_open_id?: string;
|
|
34
|
+
/** Requires feishu_identity. Returned together with feishu_tenant_key for cross-app identity linking. */
|
|
35
|
+
feishu_union_id?: string;
|
|
36
|
+
/** Enterprise identity namespace; only returned together with feishu_union_id. */
|
|
37
|
+
feishu_tenant_key?: string;
|
|
33
38
|
roles?: string[];
|
|
34
39
|
[key: string]: unknown;
|
|
35
40
|
}
|
|
41
|
+
/** Sensitive enterprise attributes returned only when the current client and user grant the matching scopes. */
|
|
42
|
+
interface EnterpriseDepartmentPathItem {
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
}
|
|
46
|
+
type EnterpriseDepartmentLeaderType = 'primary' | 'deputy';
|
|
47
|
+
interface EnterpriseDepartmentLeader {
|
|
48
|
+
/** Scoped to the same Feishu app as UserInfo.feishu_open_id. */
|
|
49
|
+
feishu_open_id?: string;
|
|
50
|
+
/** @deprecated Use feishu_open_id. Kept as an equal-valued alias for 2.x compatibility. */
|
|
51
|
+
open_id: string;
|
|
52
|
+
name: string | null;
|
|
53
|
+
avatar_url: string | null;
|
|
54
|
+
/** primary = 主负责人;deputy = 副负责人。 */
|
|
55
|
+
leader_type: EnterpriseDepartmentLeaderType;
|
|
56
|
+
}
|
|
57
|
+
interface EnterpriseProfile {
|
|
58
|
+
phone_number?: string | null;
|
|
59
|
+
employee_number?: string | null;
|
|
60
|
+
primary_department_id?: string | null;
|
|
61
|
+
primary_department_name?: string | null;
|
|
62
|
+
/** Ordered from the top-level department to the user's primary (leaf) department. */
|
|
63
|
+
primary_department_path?: EnterpriseDepartmentPathItem[] | null;
|
|
64
|
+
/** Human-readable path such as "一级部门 / 二级部门 / 叶子部门". */
|
|
65
|
+
primary_department_path_name?: string | null;
|
|
66
|
+
/** Main and deputy leaders of the primary department. */
|
|
67
|
+
primary_department_leaders?: EnterpriseDepartmentLeader[] | null;
|
|
68
|
+
}
|
|
69
|
+
interface EnterpriseProfileErrorBody {
|
|
70
|
+
statusCode?: number;
|
|
71
|
+
code?: string;
|
|
72
|
+
message?: string | string[];
|
|
73
|
+
retryable?: boolean;
|
|
74
|
+
}
|
|
36
75
|
interface AuthState {
|
|
37
76
|
isAuthenticated: boolean;
|
|
38
77
|
user: UserInfo | null;
|
|
@@ -49,6 +88,16 @@ interface PKCEPair {
|
|
|
49
88
|
codeChallenge: string;
|
|
50
89
|
}
|
|
51
90
|
|
|
91
|
+
declare class EnterpriseProfileError extends Error {
|
|
92
|
+
readonly status: number;
|
|
93
|
+
readonly code?: string;
|
|
94
|
+
readonly retryable: boolean;
|
|
95
|
+
constructor(message: string, options: {
|
|
96
|
+
status: number;
|
|
97
|
+
code?: string;
|
|
98
|
+
retryable?: boolean;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
52
101
|
declare class IdaasClient {
|
|
53
102
|
private config;
|
|
54
103
|
private endpoints;
|
|
@@ -62,6 +111,11 @@ declare class IdaasClient {
|
|
|
62
111
|
handleCallback(callbackUrl?: string): Promise<TokenSet>;
|
|
63
112
|
refreshToken(): Promise<TokenSet | null>;
|
|
64
113
|
getUserInfo(forceRefresh?: boolean): Promise<UserInfo | null>;
|
|
114
|
+
/**
|
|
115
|
+
* Fetch sensitive attributes for the current user. The server enforces both
|
|
116
|
+
* the client's scope allowlist and the user's consent before returning fields.
|
|
117
|
+
*/
|
|
118
|
+
getEnterpriseProfile(): Promise<EnterpriseProfile | null>;
|
|
65
119
|
getAccessToken(): Promise<string | null>;
|
|
66
120
|
getServiceToken(serviceKey: string): Promise<string | null>;
|
|
67
121
|
getIdToken(): string | null;
|
|
@@ -100,4 +154,4 @@ declare class TokenStorage {
|
|
|
100
154
|
clearAuthState(): void;
|
|
101
155
|
}
|
|
102
156
|
|
|
103
|
-
export { type AuthState, IdaasClient, type IdaasConfig, type ServiceTokenState, type SessionLogoutOptions, type TokenSet, TokenStorage, type UserInfo, createPKCE, generateNonce, generateState };
|
|
157
|
+
export { type AuthState, type EnterpriseDepartmentLeader, type EnterpriseDepartmentLeaderType, type EnterpriseDepartmentPathItem, type EnterpriseProfile, EnterpriseProfileError, type EnterpriseProfileErrorBody, IdaasClient, type IdaasConfig, type ServiceTokenState, type SessionLogoutOptions, type TokenSet, TokenStorage, type UserInfo, createPKCE, generateNonce, generateState };
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
EnterpriseProfileError: () => EnterpriseProfileError,
|
|
23
24
|
IdaasClient: () => IdaasClient,
|
|
24
25
|
TokenStorage: () => TokenStorage,
|
|
25
26
|
createPKCE: () => createPKCE,
|
|
@@ -269,6 +270,15 @@ var TokenStorage = class {
|
|
|
269
270
|
};
|
|
270
271
|
|
|
271
272
|
// src/client.ts
|
|
273
|
+
var EnterpriseProfileError = class extends Error {
|
|
274
|
+
constructor(message, options) {
|
|
275
|
+
super(message);
|
|
276
|
+
this.name = "EnterpriseProfileError";
|
|
277
|
+
this.status = options.status;
|
|
278
|
+
this.code = options.code;
|
|
279
|
+
this.retryable = options.retryable ?? options.status >= 500;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
272
282
|
var IdaasClient = class {
|
|
273
283
|
constructor(config) {
|
|
274
284
|
this.endpoints = null;
|
|
@@ -290,6 +300,7 @@ var IdaasClient = class {
|
|
|
290
300
|
authorization_endpoint: `${this.config.issuer}/oauth2/authorize`,
|
|
291
301
|
token_endpoint: `${this.config.issuer}/oauth2/token`,
|
|
292
302
|
userinfo_endpoint: `${this.config.issuer}/oauth2/userinfo`,
|
|
303
|
+
enterprise_profile_endpoint: `${this.config.issuer}/oauth2/enterprise-profile`,
|
|
293
304
|
end_session_endpoint: `${this.config.issuer}/oauth2/logout`,
|
|
294
305
|
jwks_uri: `${this.config.issuer}/.well-known/jwks.json`,
|
|
295
306
|
revocation_endpoint: `${this.config.issuer}/oauth2/revoke`,
|
|
@@ -307,6 +318,7 @@ var IdaasClient = class {
|
|
|
307
318
|
authorization_endpoint: doc.authorization_endpoint,
|
|
308
319
|
token_endpoint: doc.token_endpoint,
|
|
309
320
|
userinfo_endpoint: doc.userinfo_endpoint,
|
|
321
|
+
enterprise_profile_endpoint: doc.enterprise_profile_endpoint || `${this.config.issuer}/oauth2/enterprise-profile`,
|
|
310
322
|
end_session_endpoint: doc.end_session_endpoint,
|
|
311
323
|
jwks_uri: doc.jwks_uri,
|
|
312
324
|
revocation_endpoint: doc.revocation_endpoint,
|
|
@@ -358,9 +370,6 @@ var IdaasClient = class {
|
|
|
358
370
|
client_id: this.config.clientId,
|
|
359
371
|
code_verifier: codeVerifier
|
|
360
372
|
});
|
|
361
|
-
if (this.config.clientSecret) {
|
|
362
|
-
body.set("client_secret", this.config.clientSecret);
|
|
363
|
-
}
|
|
364
373
|
const res = await fetch(this.endpoints.token_endpoint, {
|
|
365
374
|
method: "POST",
|
|
366
375
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
@@ -384,9 +393,6 @@ var IdaasClient = class {
|
|
|
384
393
|
refresh_token: refreshToken,
|
|
385
394
|
client_id: this.config.clientId
|
|
386
395
|
});
|
|
387
|
-
if (this.config.clientSecret) {
|
|
388
|
-
body.set("client_secret", this.config.clientSecret);
|
|
389
|
-
}
|
|
390
396
|
const res = await fetch(this.endpoints.token_endpoint, {
|
|
391
397
|
method: "POST",
|
|
392
398
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
@@ -412,6 +418,28 @@ var IdaasClient = class {
|
|
|
412
418
|
this.userInfo = await res.json();
|
|
413
419
|
return this.userInfo;
|
|
414
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* Fetch sensitive attributes for the current user. The server enforces both
|
|
423
|
+
* the client's scope allowlist and the user's consent before returning fields.
|
|
424
|
+
*/
|
|
425
|
+
async getEnterpriseProfile() {
|
|
426
|
+
if (!this.endpoints) await this.init();
|
|
427
|
+
const accessToken = await this.getAccessToken();
|
|
428
|
+
if (!accessToken) return null;
|
|
429
|
+
const res = await fetch(this.endpoints.enterprise_profile_endpoint, {
|
|
430
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
431
|
+
});
|
|
432
|
+
if (!res.ok) {
|
|
433
|
+
const body = await res.json().catch(() => ({}));
|
|
434
|
+
const message = Array.isArray(body.message) ? body.message.join(", ") : body.message || "Enterprise profile is temporarily unavailable";
|
|
435
|
+
throw new EnterpriseProfileError(message, {
|
|
436
|
+
status: res.status,
|
|
437
|
+
code: body.code,
|
|
438
|
+
retryable: body.retryable
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
return await res.json();
|
|
442
|
+
}
|
|
415
443
|
async getAccessToken() {
|
|
416
444
|
const token = this.storage.getAccessToken();
|
|
417
445
|
if (token && !this.storage.isTokenExpired()) return token;
|
|
@@ -519,6 +547,7 @@ var IdaasClient = class {
|
|
|
519
547
|
};
|
|
520
548
|
// Annotate the CommonJS export names for ESM import in node:
|
|
521
549
|
0 && (module.exports = {
|
|
550
|
+
EnterpriseProfileError,
|
|
522
551
|
IdaasClient,
|
|
523
552
|
TokenStorage,
|
|
524
553
|
createPKCE,
|
package/dist/index.mjs
CHANGED
|
@@ -239,6 +239,15 @@ var TokenStorage = class {
|
|
|
239
239
|
};
|
|
240
240
|
|
|
241
241
|
// src/client.ts
|
|
242
|
+
var EnterpriseProfileError = class extends Error {
|
|
243
|
+
constructor(message, options) {
|
|
244
|
+
super(message);
|
|
245
|
+
this.name = "EnterpriseProfileError";
|
|
246
|
+
this.status = options.status;
|
|
247
|
+
this.code = options.code;
|
|
248
|
+
this.retryable = options.retryable ?? options.status >= 500;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
242
251
|
var IdaasClient = class {
|
|
243
252
|
constructor(config) {
|
|
244
253
|
this.endpoints = null;
|
|
@@ -260,6 +269,7 @@ var IdaasClient = class {
|
|
|
260
269
|
authorization_endpoint: `${this.config.issuer}/oauth2/authorize`,
|
|
261
270
|
token_endpoint: `${this.config.issuer}/oauth2/token`,
|
|
262
271
|
userinfo_endpoint: `${this.config.issuer}/oauth2/userinfo`,
|
|
272
|
+
enterprise_profile_endpoint: `${this.config.issuer}/oauth2/enterprise-profile`,
|
|
263
273
|
end_session_endpoint: `${this.config.issuer}/oauth2/logout`,
|
|
264
274
|
jwks_uri: `${this.config.issuer}/.well-known/jwks.json`,
|
|
265
275
|
revocation_endpoint: `${this.config.issuer}/oauth2/revoke`,
|
|
@@ -277,6 +287,7 @@ var IdaasClient = class {
|
|
|
277
287
|
authorization_endpoint: doc.authorization_endpoint,
|
|
278
288
|
token_endpoint: doc.token_endpoint,
|
|
279
289
|
userinfo_endpoint: doc.userinfo_endpoint,
|
|
290
|
+
enterprise_profile_endpoint: doc.enterprise_profile_endpoint || `${this.config.issuer}/oauth2/enterprise-profile`,
|
|
280
291
|
end_session_endpoint: doc.end_session_endpoint,
|
|
281
292
|
jwks_uri: doc.jwks_uri,
|
|
282
293
|
revocation_endpoint: doc.revocation_endpoint,
|
|
@@ -328,9 +339,6 @@ var IdaasClient = class {
|
|
|
328
339
|
client_id: this.config.clientId,
|
|
329
340
|
code_verifier: codeVerifier
|
|
330
341
|
});
|
|
331
|
-
if (this.config.clientSecret) {
|
|
332
|
-
body.set("client_secret", this.config.clientSecret);
|
|
333
|
-
}
|
|
334
342
|
const res = await fetch(this.endpoints.token_endpoint, {
|
|
335
343
|
method: "POST",
|
|
336
344
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
@@ -354,9 +362,6 @@ var IdaasClient = class {
|
|
|
354
362
|
refresh_token: refreshToken,
|
|
355
363
|
client_id: this.config.clientId
|
|
356
364
|
});
|
|
357
|
-
if (this.config.clientSecret) {
|
|
358
|
-
body.set("client_secret", this.config.clientSecret);
|
|
359
|
-
}
|
|
360
365
|
const res = await fetch(this.endpoints.token_endpoint, {
|
|
361
366
|
method: "POST",
|
|
362
367
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
@@ -382,6 +387,28 @@ var IdaasClient = class {
|
|
|
382
387
|
this.userInfo = await res.json();
|
|
383
388
|
return this.userInfo;
|
|
384
389
|
}
|
|
390
|
+
/**
|
|
391
|
+
* Fetch sensitive attributes for the current user. The server enforces both
|
|
392
|
+
* the client's scope allowlist and the user's consent before returning fields.
|
|
393
|
+
*/
|
|
394
|
+
async getEnterpriseProfile() {
|
|
395
|
+
if (!this.endpoints) await this.init();
|
|
396
|
+
const accessToken = await this.getAccessToken();
|
|
397
|
+
if (!accessToken) return null;
|
|
398
|
+
const res = await fetch(this.endpoints.enterprise_profile_endpoint, {
|
|
399
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
400
|
+
});
|
|
401
|
+
if (!res.ok) {
|
|
402
|
+
const body = await res.json().catch(() => ({}));
|
|
403
|
+
const message = Array.isArray(body.message) ? body.message.join(", ") : body.message || "Enterprise profile is temporarily unavailable";
|
|
404
|
+
throw new EnterpriseProfileError(message, {
|
|
405
|
+
status: res.status,
|
|
406
|
+
code: body.code,
|
|
407
|
+
retryable: body.retryable
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
return await res.json();
|
|
411
|
+
}
|
|
385
412
|
async getAccessToken() {
|
|
386
413
|
const token = this.storage.getAccessToken();
|
|
387
414
|
if (token && !this.storage.isTokenExpired()) return token;
|
|
@@ -488,6 +515,7 @@ var IdaasClient = class {
|
|
|
488
515
|
}
|
|
489
516
|
};
|
|
490
517
|
export {
|
|
518
|
+
EnterpriseProfileError,
|
|
491
519
|
IdaasClient,
|
|
492
520
|
TokenStorage,
|
|
493
521
|
createPKCE,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zskj-idaas/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "IDaaS OIDC Client SDK for browser applications — supports Authorization Code Flow with PKCE, token management, and authenticated fetch.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|