@nihplod/sso-sdk 1.5.0 → 1.7.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 +21 -2
- package/dist/index.d.mts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +148 -84
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +148 -84
- package/dist/index.mjs.map +1 -1
- package/dist/next/index.d.mts +87 -2
- package/dist/next/index.d.ts +87 -2
- package/dist/next/index.js +330 -64
- package/dist/next/index.js.map +1 -1
- package/dist/next/index.mjs +329 -64
- package/dist/next/index.mjs.map +1 -1
- package/dist/react/index.d.mts +32 -2
- package/dist/react/index.d.ts +32 -2
- package/dist/react/index.js +196 -105
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +196 -105
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react/index.d.mts
CHANGED
|
@@ -58,6 +58,17 @@ interface SsoClientConfig {
|
|
|
58
58
|
redirectUri: string;
|
|
59
59
|
/** SSO 中心地址,如 "https://nihplod.cn" */
|
|
60
60
|
ssoBaseUrl: string;
|
|
61
|
+
/**
|
|
62
|
+
* 允许 Discovery 文档声明跨源端点(默认 false)。
|
|
63
|
+
*
|
|
64
|
+
* ⚠️ 安全警告:Discovery 文档若被篡改(如中间人/反向代理劫持),攻击者可把
|
|
65
|
+
* token_endpoint 指向自己的服务器窃取 authorization code / refresh_token。
|
|
66
|
+
* SDK 默认校验 authorization_endpoint 必须与 ssoBaseUrl 同源、
|
|
67
|
+
* token_endpoint / userinfo_endpoint 必须与 ssoBaseUrl(或 serverBaseUrl)同源,
|
|
68
|
+
* 不匹配时告警并回退到默认端点。仅当 SSO 中心确实把端点托管在其他域名时
|
|
69
|
+
* 才设置此开关,并自行确保 Discovery 通道可信。
|
|
70
|
+
*/
|
|
71
|
+
allowCrossOriginDiscoveryEndpoints?: boolean;
|
|
61
72
|
/** 请求的 scope(空格分隔),如 "openid profile phone" */
|
|
62
73
|
scopes?: string;
|
|
63
74
|
/** RP-Initiated Logout 返回地址(可选)。不传时回退到 redirectUri */
|
|
@@ -128,6 +139,8 @@ declare class SsoClient {
|
|
|
128
139
|
private _discovery;
|
|
129
140
|
private _discoveryFetchedAt;
|
|
130
141
|
private _refreshLock;
|
|
142
|
+
/** Discovery 端点 origin 校验失败只告警一次(拒绝的文档不缓存,避免每次调用刷屏) */
|
|
143
|
+
private _discoveryOriginWarned;
|
|
131
144
|
/** Discovery 文档缓存 TTL(5 分钟) */
|
|
132
145
|
private static readonly DISCOVERY_TTL_MS;
|
|
133
146
|
/** Discovery fetch 超时(10 秒) */
|
|
@@ -139,6 +152,15 @@ declare class SsoClient {
|
|
|
139
152
|
* 不会输出 token/授权码等敏感值。
|
|
140
153
|
*/
|
|
141
154
|
debugLog(...args: unknown[]): void;
|
|
155
|
+
/**
|
|
156
|
+
* 校验 Discovery 文档端点 origin(防文档被篡改后 token/code 被导向攻击者服务器):
|
|
157
|
+
* - authorization_endpoint 必须与 ssoBaseUrl 同源(浏览器跳转目标);
|
|
158
|
+
* - token_endpoint / userinfo_endpoint 必须与 ssoBaseUrl 同源,
|
|
159
|
+
* 或(配置了 serverBaseUrl 时)与内网地址同源。
|
|
160
|
+
* 任一不匹配:告警并返回 null(上层回退到默认端点,fail-safe)。
|
|
161
|
+
* 可通过 config.allowCrossOriginDiscoveryEndpoints 关闭(见配置项警告)。
|
|
162
|
+
*/
|
|
163
|
+
private _sanitizeDiscovery;
|
|
142
164
|
/**
|
|
143
165
|
* 获取 OIDC Discovery 文档(带缓存 + 超时)
|
|
144
166
|
*
|
|
@@ -260,6 +282,9 @@ declare class SsoClient {
|
|
|
260
282
|
* 检查是否已认证(不发起网络请求)
|
|
261
283
|
*
|
|
262
284
|
* 仅检查本地是否存在未过期的 access_token。
|
|
285
|
+
* 故意不做 EXPIRY_SKEW_MS 提前量:这是同步的 UI 提示判定(是否显示"已登录"),
|
|
286
|
+
* 提前 10s 翻转 false 会让调用方在 token 仍有效时误显示未登录;真正发请求的
|
|
287
|
+
* getAccessToken/getUserInfo 已带提前量。
|
|
263
288
|
*/
|
|
264
289
|
isAuthenticated(): boolean;
|
|
265
290
|
/**
|
|
@@ -301,7 +326,7 @@ declare class SsoClient {
|
|
|
301
326
|
* SSO SDK 错误类型
|
|
302
327
|
*/
|
|
303
328
|
/** SSO 错误码 */
|
|
304
|
-
type SsoErrorCode = "invalid_config" | "state_mismatch" | "pkce_required" | "token_request_failed" | "session_expired" | "no_refresh_token" | "userinfo_failed" | "not_authenticated" | "authorization_code_expired" | "authorization_code_used" | "client_disabled" | "user_denied_authorization" | "account_disabled" | "sso_server_error" | "rate_limited" | "network_error" | "popup_blocked" | "popup_closed" | "id_token_invalid" | "id_token_unsupported_alg" | "id_token_hs256_unsupported" | "id_token_missing_secret" | "id_token_invalid_signature" | "id_token_issuer_mismatch" | "id_token_audience_mismatch" | "id_token_expired" | "id_token_missing_sub" | "id_token_at_hash_mismatch" | "id_token_nonce_mismatch" | "logout_token_invalid" | "logout_token_unsupported_alg" | "logout_token_invalid_signature" | "logout_token_issuer_mismatch" | "logout_token_audience_mismatch" | "logout_token_expired" | "logout_token_replay";
|
|
329
|
+
type SsoErrorCode = "invalid_config" | "state_mismatch" | "issuer_mismatch" | "pkce_required" | "token_request_failed" | "session_expired" | "no_refresh_token" | "userinfo_failed" | "not_authenticated" | "authorization_code_expired" | "authorization_code_used" | "client_disabled" | "user_denied_authorization" | "account_disabled" | "sso_server_error" | "rate_limited" | "network_error" | "popup_blocked" | "popup_closed" | "id_token_invalid" | "id_token_unsupported_alg" | "id_token_hs256_unsupported" | "id_token_missing_secret" | "id_token_invalid_signature" | "id_token_issuer_mismatch" | "id_token_audience_mismatch" | "id_token_expired" | "id_token_missing_sub" | "id_token_at_hash_mismatch" | "id_token_nonce_mismatch" | "logout_token_invalid" | "logout_token_unsupported_alg" | "logout_token_invalid_signature" | "logout_token_issuer_mismatch" | "logout_token_audience_mismatch" | "logout_token_expired" | "logout_token_replay";
|
|
305
330
|
/**
|
|
306
331
|
* SSO SDK 自定义错误
|
|
307
332
|
*/
|
|
@@ -431,6 +456,11 @@ interface RequireAuthProps {
|
|
|
431
456
|
*/
|
|
432
457
|
renderLoginError?: (error: unknown, retry: () => void) => React.ReactNode;
|
|
433
458
|
}
|
|
459
|
+
interface LoginErrorContentProps {
|
|
460
|
+
loginError: unknown;
|
|
461
|
+
retry: () => void;
|
|
462
|
+
renderLoginError?: RequireAuthProps["renderLoginError"];
|
|
463
|
+
}
|
|
434
464
|
/**
|
|
435
465
|
* 路由保护组件
|
|
436
466
|
*
|
|
@@ -445,7 +475,7 @@ interface RequireAuthProps {
|
|
|
445
475
|
* </RequireAuth>
|
|
446
476
|
* ```
|
|
447
477
|
*/
|
|
448
|
-
declare function RequireAuth({ children, fallback, autoLogin, usePopup, onError, renderLoginError, }: RequireAuthProps): string | number | bigint | true | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.FunctionComponentElement<React.FragmentProps>;
|
|
478
|
+
declare function RequireAuth({ children, fallback, autoLogin, usePopup, onError, renderLoginError, }: RequireAuthProps): string | number | bigint | true | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.FunctionComponentElement<React.FragmentProps> | React.FunctionComponentElement<LoginErrorContentProps>;
|
|
449
479
|
/**
|
|
450
480
|
* 高阶组件:包装页面组件,要求认证后才能访问
|
|
451
481
|
*
|
package/dist/react/index.d.ts
CHANGED
|
@@ -58,6 +58,17 @@ interface SsoClientConfig {
|
|
|
58
58
|
redirectUri: string;
|
|
59
59
|
/** SSO 中心地址,如 "https://nihplod.cn" */
|
|
60
60
|
ssoBaseUrl: string;
|
|
61
|
+
/**
|
|
62
|
+
* 允许 Discovery 文档声明跨源端点(默认 false)。
|
|
63
|
+
*
|
|
64
|
+
* ⚠️ 安全警告:Discovery 文档若被篡改(如中间人/反向代理劫持),攻击者可把
|
|
65
|
+
* token_endpoint 指向自己的服务器窃取 authorization code / refresh_token。
|
|
66
|
+
* SDK 默认校验 authorization_endpoint 必须与 ssoBaseUrl 同源、
|
|
67
|
+
* token_endpoint / userinfo_endpoint 必须与 ssoBaseUrl(或 serverBaseUrl)同源,
|
|
68
|
+
* 不匹配时告警并回退到默认端点。仅当 SSO 中心确实把端点托管在其他域名时
|
|
69
|
+
* 才设置此开关,并自行确保 Discovery 通道可信。
|
|
70
|
+
*/
|
|
71
|
+
allowCrossOriginDiscoveryEndpoints?: boolean;
|
|
61
72
|
/** 请求的 scope(空格分隔),如 "openid profile phone" */
|
|
62
73
|
scopes?: string;
|
|
63
74
|
/** RP-Initiated Logout 返回地址(可选)。不传时回退到 redirectUri */
|
|
@@ -128,6 +139,8 @@ declare class SsoClient {
|
|
|
128
139
|
private _discovery;
|
|
129
140
|
private _discoveryFetchedAt;
|
|
130
141
|
private _refreshLock;
|
|
142
|
+
/** Discovery 端点 origin 校验失败只告警一次(拒绝的文档不缓存,避免每次调用刷屏) */
|
|
143
|
+
private _discoveryOriginWarned;
|
|
131
144
|
/** Discovery 文档缓存 TTL(5 分钟) */
|
|
132
145
|
private static readonly DISCOVERY_TTL_MS;
|
|
133
146
|
/** Discovery fetch 超时(10 秒) */
|
|
@@ -139,6 +152,15 @@ declare class SsoClient {
|
|
|
139
152
|
* 不会输出 token/授权码等敏感值。
|
|
140
153
|
*/
|
|
141
154
|
debugLog(...args: unknown[]): void;
|
|
155
|
+
/**
|
|
156
|
+
* 校验 Discovery 文档端点 origin(防文档被篡改后 token/code 被导向攻击者服务器):
|
|
157
|
+
* - authorization_endpoint 必须与 ssoBaseUrl 同源(浏览器跳转目标);
|
|
158
|
+
* - token_endpoint / userinfo_endpoint 必须与 ssoBaseUrl 同源,
|
|
159
|
+
* 或(配置了 serverBaseUrl 时)与内网地址同源。
|
|
160
|
+
* 任一不匹配:告警并返回 null(上层回退到默认端点,fail-safe)。
|
|
161
|
+
* 可通过 config.allowCrossOriginDiscoveryEndpoints 关闭(见配置项警告)。
|
|
162
|
+
*/
|
|
163
|
+
private _sanitizeDiscovery;
|
|
142
164
|
/**
|
|
143
165
|
* 获取 OIDC Discovery 文档(带缓存 + 超时)
|
|
144
166
|
*
|
|
@@ -260,6 +282,9 @@ declare class SsoClient {
|
|
|
260
282
|
* 检查是否已认证(不发起网络请求)
|
|
261
283
|
*
|
|
262
284
|
* 仅检查本地是否存在未过期的 access_token。
|
|
285
|
+
* 故意不做 EXPIRY_SKEW_MS 提前量:这是同步的 UI 提示判定(是否显示"已登录"),
|
|
286
|
+
* 提前 10s 翻转 false 会让调用方在 token 仍有效时误显示未登录;真正发请求的
|
|
287
|
+
* getAccessToken/getUserInfo 已带提前量。
|
|
263
288
|
*/
|
|
264
289
|
isAuthenticated(): boolean;
|
|
265
290
|
/**
|
|
@@ -301,7 +326,7 @@ declare class SsoClient {
|
|
|
301
326
|
* SSO SDK 错误类型
|
|
302
327
|
*/
|
|
303
328
|
/** SSO 错误码 */
|
|
304
|
-
type SsoErrorCode = "invalid_config" | "state_mismatch" | "pkce_required" | "token_request_failed" | "session_expired" | "no_refresh_token" | "userinfo_failed" | "not_authenticated" | "authorization_code_expired" | "authorization_code_used" | "client_disabled" | "user_denied_authorization" | "account_disabled" | "sso_server_error" | "rate_limited" | "network_error" | "popup_blocked" | "popup_closed" | "id_token_invalid" | "id_token_unsupported_alg" | "id_token_hs256_unsupported" | "id_token_missing_secret" | "id_token_invalid_signature" | "id_token_issuer_mismatch" | "id_token_audience_mismatch" | "id_token_expired" | "id_token_missing_sub" | "id_token_at_hash_mismatch" | "id_token_nonce_mismatch" | "logout_token_invalid" | "logout_token_unsupported_alg" | "logout_token_invalid_signature" | "logout_token_issuer_mismatch" | "logout_token_audience_mismatch" | "logout_token_expired" | "logout_token_replay";
|
|
329
|
+
type SsoErrorCode = "invalid_config" | "state_mismatch" | "issuer_mismatch" | "pkce_required" | "token_request_failed" | "session_expired" | "no_refresh_token" | "userinfo_failed" | "not_authenticated" | "authorization_code_expired" | "authorization_code_used" | "client_disabled" | "user_denied_authorization" | "account_disabled" | "sso_server_error" | "rate_limited" | "network_error" | "popup_blocked" | "popup_closed" | "id_token_invalid" | "id_token_unsupported_alg" | "id_token_hs256_unsupported" | "id_token_missing_secret" | "id_token_invalid_signature" | "id_token_issuer_mismatch" | "id_token_audience_mismatch" | "id_token_expired" | "id_token_missing_sub" | "id_token_at_hash_mismatch" | "id_token_nonce_mismatch" | "logout_token_invalid" | "logout_token_unsupported_alg" | "logout_token_invalid_signature" | "logout_token_issuer_mismatch" | "logout_token_audience_mismatch" | "logout_token_expired" | "logout_token_replay";
|
|
305
330
|
/**
|
|
306
331
|
* SSO SDK 自定义错误
|
|
307
332
|
*/
|
|
@@ -431,6 +456,11 @@ interface RequireAuthProps {
|
|
|
431
456
|
*/
|
|
432
457
|
renderLoginError?: (error: unknown, retry: () => void) => React.ReactNode;
|
|
433
458
|
}
|
|
459
|
+
interface LoginErrorContentProps {
|
|
460
|
+
loginError: unknown;
|
|
461
|
+
retry: () => void;
|
|
462
|
+
renderLoginError?: RequireAuthProps["renderLoginError"];
|
|
463
|
+
}
|
|
434
464
|
/**
|
|
435
465
|
* 路由保护组件
|
|
436
466
|
*
|
|
@@ -445,7 +475,7 @@ interface RequireAuthProps {
|
|
|
445
475
|
* </RequireAuth>
|
|
446
476
|
* ```
|
|
447
477
|
*/
|
|
448
|
-
declare function RequireAuth({ children, fallback, autoLogin, usePopup, onError, renderLoginError, }: RequireAuthProps): string | number | bigint | true | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.FunctionComponentElement<React.FragmentProps>;
|
|
478
|
+
declare function RequireAuth({ children, fallback, autoLogin, usePopup, onError, renderLoginError, }: RequireAuthProps): string | number | bigint | true | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.FunctionComponentElement<React.FragmentProps> | React.FunctionComponentElement<LoginErrorContentProps>;
|
|
449
479
|
/**
|
|
450
480
|
* 高阶组件:包装页面组件,要求认证后才能访问
|
|
451
481
|
*
|