@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 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://your-idaas-server.com',
32
- clientId: 'your-client-id',
33
- redirectUri: 'http://localhost:3000/callback',
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@example.com' });
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
- 带认证的 fetch 封装,自动附加 Authorization 头。
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.fetch('/api/protected-resource');
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://idaas.example.com',
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.fetch('https://api.example.com/data');
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
- phone_number?: string;
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
- phone_number?: string;
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": "1.0.4",
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",