@nihplod/sso-sdk 1.1.0 → 1.2.1
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 +73 -6
- package/dist/index.d.mts +51 -10
- package/dist/index.d.ts +51 -10
- package/dist/index.js +68 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +65 -9
- package/dist/index.mjs.map +1 -1
- package/dist/next/index.d.mts +38 -5
- package/dist/next/index.d.ts +38 -5
- package/dist/next/index.js +90 -49
- package/dist/next/index.js.map +1 -1
- package/dist/next/index.mjs +88 -48
- package/dist/next/index.mjs.map +1 -1
- package/dist/react/index.d.mts +113 -10
- package/dist/react/index.d.ts +113 -10
- package/dist/react/index.js +166 -55
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +169 -59
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/next/index.d.mts
CHANGED
|
@@ -61,6 +61,18 @@ interface SsoMiddlewareConfig {
|
|
|
61
61
|
returnUrlCookieName?: string;
|
|
62
62
|
/** PKCE Verifier Cookie 名称,默认 __Secure-nihplod_sso_verifier */
|
|
63
63
|
verifierCookieName?: string;
|
|
64
|
+
/**
|
|
65
|
+
* 本地 HTTP 开发模式(默认 false)。
|
|
66
|
+
*
|
|
67
|
+
* ⚠️ 仅限 http://localhost 开发:关闭 Cookie 的 Secure 属性并去除
|
|
68
|
+
* __Host-/__Secure- 前缀(浏览器拒绝在 HTTP 下写入带这两个前缀的 Cookie,
|
|
69
|
+
* 否则会出现「登录后 cookie 写不进去 → middleware 永远判定未登录 →
|
|
70
|
+
* 反复跳 SSO」的无限重定向)。开启时启动告警;生产环境严禁启用——
|
|
71
|
+
* 生产环境(NODE_ENV=production 且 ssoBaseUrl 为 https)下该开关会被
|
|
72
|
+
* 强制忽略并告警,仍走 secure cookie。
|
|
73
|
+
* middleware / callback / logout 三处配置需保持一致。
|
|
74
|
+
*/
|
|
75
|
+
insecureLocalDev?: boolean;
|
|
64
76
|
}
|
|
65
77
|
declare function createSsoMiddleware(config: SsoMiddlewareConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
66
78
|
|
|
@@ -115,6 +127,14 @@ interface CallbackRouteConfig {
|
|
|
115
127
|
returnUrlCookieName?: string;
|
|
116
128
|
/** PKCE Verifier Cookie 名称,默认 __Secure-nihplod_sso_verifier */
|
|
117
129
|
verifierCookieName?: string;
|
|
130
|
+
/**
|
|
131
|
+
* 本地 HTTP 开发模式(默认 false)。关闭 Cookie 的 Secure 属性并去除
|
|
132
|
+
* __Host-/__Secure- 前缀;必须与 createSsoMiddleware 的配置保持一致,
|
|
133
|
+
* 否则读不到 middleware 写入的 state/verifier Cookie。生产严禁启用——
|
|
134
|
+
* 生产环境(NODE_ENV=production 且 ssoBaseUrl 为 https)下该开关会被
|
|
135
|
+
* 强制忽略并告警。
|
|
136
|
+
*/
|
|
137
|
+
insecureLocalDev?: boolean;
|
|
118
138
|
}
|
|
119
139
|
declare function createCallbackRouteHandler(config: CallbackRouteConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
120
140
|
|
|
@@ -176,6 +196,13 @@ interface LogoutRouteConfig {
|
|
|
176
196
|
callbackPath?: string;
|
|
177
197
|
/** Logout State Cookie 名称(RP-Initiated Logout CSRF 防护),默认 __Host-nihplod_sso_logout_state */
|
|
178
198
|
logoutStateCookieName?: string;
|
|
199
|
+
/**
|
|
200
|
+
* 本地 HTTP 开发模式(默认 false)。关闭 Cookie 的 Secure 属性并去除
|
|
201
|
+
* __Host-/__Secure- 前缀;必须与 middleware / callback 的配置保持一致,
|
|
202
|
+
* 否则无法清除它们写入的 Cookie。生产严禁启用——生产环境
|
|
203
|
+
* (NODE_ENV=production 且 ssoBaseUrl 为 https)下该开关会被强制忽略并告警。
|
|
204
|
+
*/
|
|
205
|
+
insecureLocalDev?: boolean;
|
|
179
206
|
}
|
|
180
207
|
declare function createLogoutRouteHandler(config: LogoutRouteConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
181
208
|
|
|
@@ -196,21 +223,27 @@ declare const DEFAULT_STATE_COOKIE_NAME = "__Host-nihplod_sso_state";
|
|
|
196
223
|
declare const DEFAULT_RETURN_COOKIE_NAME = "__Host-nihplod_sso_return";
|
|
197
224
|
declare const DEFAULT_VERIFIER_COOKIE_NAME = "__Secure-nihplod_sso_verifier";
|
|
198
225
|
declare const DEFAULT_LOGOUT_STATE_COOKIE_NAME = "__Host-nihplod_sso_logout_state";
|
|
226
|
+
/**
|
|
227
|
+
* insecureLocalDev 场景:去除 __Host-/__Secure- 前缀。
|
|
228
|
+
* 浏览器强制要求带这两个前缀的 Cookie 必须设置 Secure,
|
|
229
|
+
* HTTP 本地开发时若保留前缀,即使 secure=false 也会被拒绝写入。
|
|
230
|
+
*/
|
|
231
|
+
declare function toInsecureCookieName(name: string): string;
|
|
199
232
|
/** __Host- 前缀 Cookie 的安全选项(Path 必须为 /) */
|
|
200
|
-
declare function getHostCookieOptions(maxAge?: number): {
|
|
233
|
+
declare function getHostCookieOptions(maxAge?: number, secure?: boolean): {
|
|
201
234
|
httpOnly: true;
|
|
202
|
-
secure:
|
|
235
|
+
secure: boolean;
|
|
203
236
|
sameSite: "lax";
|
|
204
237
|
path: "/";
|
|
205
238
|
maxAge?: number;
|
|
206
239
|
};
|
|
207
240
|
/** __Secure- 前缀 Cookie 的安全选项(允许自定义 Path) */
|
|
208
|
-
declare function getSecureCookieOptions(maxAge?: number, path?: string): {
|
|
241
|
+
declare function getSecureCookieOptions(maxAge?: number, path?: string, secure?: boolean): {
|
|
209
242
|
httpOnly: true;
|
|
210
|
-
secure:
|
|
243
|
+
secure: boolean;
|
|
211
244
|
sameSite: "lax";
|
|
212
245
|
path: string;
|
|
213
246
|
maxAge?: number;
|
|
214
247
|
};
|
|
215
248
|
|
|
216
|
-
export { type CallbackRouteConfig, DEFAULT_ACCESS_TOKEN_COOKIE_NAME, DEFAULT_ID_TOKEN_COOKIE_NAME, DEFAULT_LOGOUT_STATE_COOKIE_NAME, DEFAULT_REFRESH_TOKEN_COOKIE_NAME, DEFAULT_RETURN_COOKIE_NAME, DEFAULT_STATE_COOKIE_NAME, DEFAULT_VERIFIER_COOKIE_NAME, type LogoutRouteConfig, type SsoMiddlewareConfig, createCallbackRouteHandler, createLogoutRouteHandler, createSsoMiddleware, getHostCookieOptions, getSecureCookieOptions };
|
|
249
|
+
export { type CallbackRouteConfig, DEFAULT_ACCESS_TOKEN_COOKIE_NAME, DEFAULT_ID_TOKEN_COOKIE_NAME, DEFAULT_LOGOUT_STATE_COOKIE_NAME, DEFAULT_REFRESH_TOKEN_COOKIE_NAME, DEFAULT_RETURN_COOKIE_NAME, DEFAULT_STATE_COOKIE_NAME, DEFAULT_VERIFIER_COOKIE_NAME, type LogoutRouteConfig, type SsoMiddlewareConfig, createCallbackRouteHandler, createLogoutRouteHandler, createSsoMiddleware, getHostCookieOptions, getSecureCookieOptions, toInsecureCookieName };
|
package/dist/next/index.d.ts
CHANGED
|
@@ -61,6 +61,18 @@ interface SsoMiddlewareConfig {
|
|
|
61
61
|
returnUrlCookieName?: string;
|
|
62
62
|
/** PKCE Verifier Cookie 名称,默认 __Secure-nihplod_sso_verifier */
|
|
63
63
|
verifierCookieName?: string;
|
|
64
|
+
/**
|
|
65
|
+
* 本地 HTTP 开发模式(默认 false)。
|
|
66
|
+
*
|
|
67
|
+
* ⚠️ 仅限 http://localhost 开发:关闭 Cookie 的 Secure 属性并去除
|
|
68
|
+
* __Host-/__Secure- 前缀(浏览器拒绝在 HTTP 下写入带这两个前缀的 Cookie,
|
|
69
|
+
* 否则会出现「登录后 cookie 写不进去 → middleware 永远判定未登录 →
|
|
70
|
+
* 反复跳 SSO」的无限重定向)。开启时启动告警;生产环境严禁启用——
|
|
71
|
+
* 生产环境(NODE_ENV=production 且 ssoBaseUrl 为 https)下该开关会被
|
|
72
|
+
* 强制忽略并告警,仍走 secure cookie。
|
|
73
|
+
* middleware / callback / logout 三处配置需保持一致。
|
|
74
|
+
*/
|
|
75
|
+
insecureLocalDev?: boolean;
|
|
64
76
|
}
|
|
65
77
|
declare function createSsoMiddleware(config: SsoMiddlewareConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
66
78
|
|
|
@@ -115,6 +127,14 @@ interface CallbackRouteConfig {
|
|
|
115
127
|
returnUrlCookieName?: string;
|
|
116
128
|
/** PKCE Verifier Cookie 名称,默认 __Secure-nihplod_sso_verifier */
|
|
117
129
|
verifierCookieName?: string;
|
|
130
|
+
/**
|
|
131
|
+
* 本地 HTTP 开发模式(默认 false)。关闭 Cookie 的 Secure 属性并去除
|
|
132
|
+
* __Host-/__Secure- 前缀;必须与 createSsoMiddleware 的配置保持一致,
|
|
133
|
+
* 否则读不到 middleware 写入的 state/verifier Cookie。生产严禁启用——
|
|
134
|
+
* 生产环境(NODE_ENV=production 且 ssoBaseUrl 为 https)下该开关会被
|
|
135
|
+
* 强制忽略并告警。
|
|
136
|
+
*/
|
|
137
|
+
insecureLocalDev?: boolean;
|
|
118
138
|
}
|
|
119
139
|
declare function createCallbackRouteHandler(config: CallbackRouteConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
120
140
|
|
|
@@ -176,6 +196,13 @@ interface LogoutRouteConfig {
|
|
|
176
196
|
callbackPath?: string;
|
|
177
197
|
/** Logout State Cookie 名称(RP-Initiated Logout CSRF 防护),默认 __Host-nihplod_sso_logout_state */
|
|
178
198
|
logoutStateCookieName?: string;
|
|
199
|
+
/**
|
|
200
|
+
* 本地 HTTP 开发模式(默认 false)。关闭 Cookie 的 Secure 属性并去除
|
|
201
|
+
* __Host-/__Secure- 前缀;必须与 middleware / callback 的配置保持一致,
|
|
202
|
+
* 否则无法清除它们写入的 Cookie。生产严禁启用——生产环境
|
|
203
|
+
* (NODE_ENV=production 且 ssoBaseUrl 为 https)下该开关会被强制忽略并告警。
|
|
204
|
+
*/
|
|
205
|
+
insecureLocalDev?: boolean;
|
|
179
206
|
}
|
|
180
207
|
declare function createLogoutRouteHandler(config: LogoutRouteConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
181
208
|
|
|
@@ -196,21 +223,27 @@ declare const DEFAULT_STATE_COOKIE_NAME = "__Host-nihplod_sso_state";
|
|
|
196
223
|
declare const DEFAULT_RETURN_COOKIE_NAME = "__Host-nihplod_sso_return";
|
|
197
224
|
declare const DEFAULT_VERIFIER_COOKIE_NAME = "__Secure-nihplod_sso_verifier";
|
|
198
225
|
declare const DEFAULT_LOGOUT_STATE_COOKIE_NAME = "__Host-nihplod_sso_logout_state";
|
|
226
|
+
/**
|
|
227
|
+
* insecureLocalDev 场景:去除 __Host-/__Secure- 前缀。
|
|
228
|
+
* 浏览器强制要求带这两个前缀的 Cookie 必须设置 Secure,
|
|
229
|
+
* HTTP 本地开发时若保留前缀,即使 secure=false 也会被拒绝写入。
|
|
230
|
+
*/
|
|
231
|
+
declare function toInsecureCookieName(name: string): string;
|
|
199
232
|
/** __Host- 前缀 Cookie 的安全选项(Path 必须为 /) */
|
|
200
|
-
declare function getHostCookieOptions(maxAge?: number): {
|
|
233
|
+
declare function getHostCookieOptions(maxAge?: number, secure?: boolean): {
|
|
201
234
|
httpOnly: true;
|
|
202
|
-
secure:
|
|
235
|
+
secure: boolean;
|
|
203
236
|
sameSite: "lax";
|
|
204
237
|
path: "/";
|
|
205
238
|
maxAge?: number;
|
|
206
239
|
};
|
|
207
240
|
/** __Secure- 前缀 Cookie 的安全选项(允许自定义 Path) */
|
|
208
|
-
declare function getSecureCookieOptions(maxAge?: number, path?: string): {
|
|
241
|
+
declare function getSecureCookieOptions(maxAge?: number, path?: string, secure?: boolean): {
|
|
209
242
|
httpOnly: true;
|
|
210
|
-
secure:
|
|
243
|
+
secure: boolean;
|
|
211
244
|
sameSite: "lax";
|
|
212
245
|
path: string;
|
|
213
246
|
maxAge?: number;
|
|
214
247
|
};
|
|
215
248
|
|
|
216
|
-
export { type CallbackRouteConfig, DEFAULT_ACCESS_TOKEN_COOKIE_NAME, DEFAULT_ID_TOKEN_COOKIE_NAME, DEFAULT_LOGOUT_STATE_COOKIE_NAME, DEFAULT_REFRESH_TOKEN_COOKIE_NAME, DEFAULT_RETURN_COOKIE_NAME, DEFAULT_STATE_COOKIE_NAME, DEFAULT_VERIFIER_COOKIE_NAME, type LogoutRouteConfig, type SsoMiddlewareConfig, createCallbackRouteHandler, createLogoutRouteHandler, createSsoMiddleware, getHostCookieOptions, getSecureCookieOptions };
|
|
249
|
+
export { type CallbackRouteConfig, DEFAULT_ACCESS_TOKEN_COOKIE_NAME, DEFAULT_ID_TOKEN_COOKIE_NAME, DEFAULT_LOGOUT_STATE_COOKIE_NAME, DEFAULT_REFRESH_TOKEN_COOKIE_NAME, DEFAULT_RETURN_COOKIE_NAME, DEFAULT_STATE_COOKIE_NAME, DEFAULT_VERIFIER_COOKIE_NAME, type LogoutRouteConfig, type SsoMiddlewareConfig, createCallbackRouteHandler, createLogoutRouteHandler, createSsoMiddleware, getHostCookieOptions, getSecureCookieOptions, toInsecureCookieName };
|
package/dist/next/index.js
CHANGED
|
@@ -31,7 +31,8 @@ __export(next_exports, {
|
|
|
31
31
|
createLogoutRouteHandler: () => createLogoutRouteHandler,
|
|
32
32
|
createSsoMiddleware: () => createSsoMiddleware,
|
|
33
33
|
getHostCookieOptions: () => getHostCookieOptions,
|
|
34
|
-
getSecureCookieOptions: () => getSecureCookieOptions
|
|
34
|
+
getSecureCookieOptions: () => getSecureCookieOptions,
|
|
35
|
+
toInsecureCookieName: () => toInsecureCookieName
|
|
35
36
|
});
|
|
36
37
|
module.exports = __toCommonJS(next_exports);
|
|
37
38
|
|
|
@@ -46,19 +47,35 @@ var DEFAULT_STATE_COOKIE_NAME = "__Host-nihplod_sso_state";
|
|
|
46
47
|
var DEFAULT_RETURN_COOKIE_NAME = "__Host-nihplod_sso_return";
|
|
47
48
|
var DEFAULT_VERIFIER_COOKIE_NAME = "__Secure-nihplod_sso_verifier";
|
|
48
49
|
var DEFAULT_LOGOUT_STATE_COOKIE_NAME = "__Host-nihplod_sso_logout_state";
|
|
49
|
-
function
|
|
50
|
+
function toInsecureCookieName(name) {
|
|
51
|
+
return name.replace(/^__(Host|Secure)-/, "");
|
|
52
|
+
}
|
|
53
|
+
function resolveInsecureLocalDev(enabled, ssoBaseUrl) {
|
|
54
|
+
if (!enabled) return false;
|
|
55
|
+
if (process.env.NODE_ENV === "production" && /^https:\/\//i.test(ssoBaseUrl)) {
|
|
56
|
+
console.warn(
|
|
57
|
+
"[SSO SDK] insecureLocalDev=true \u5DF2\u88AB\u5FFD\u7565\uFF1A\u5F53\u524D\u4E3A\u751F\u4EA7\u73AF\u5883\uFF08NODE_ENV=production\uFF09\u4E14 ssoBaseUrl \u4F7F\u7528 HTTPS\uFF0CCookie \u4ECD\u4FDD\u6301 Secure \u5C5E\u6027\u4E0E __Host-/__Secure- \u524D\u7F00\u3002\u8BF7\u4ECE\u751F\u4EA7\u914D\u7F6E\u4E2D\u79FB\u9664\u8BE5\u5F00\u5173\u3002"
|
|
58
|
+
);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
console.warn(
|
|
62
|
+
"[SSO SDK] insecureLocalDev=true\uFF1ACookie \u7684 Secure \u5C5E\u6027\u5DF2\u5173\u95ED\u4E14\u53BB\u9664 __Host-/__Secure- \u524D\u7F00\u3002\u4EC5\u9650 http://localhost \u672C\u5730\u5F00\u53D1\u4F7F\u7528\uFF0C\u751F\u4EA7\u73AF\u5883\u5FC5\u987B\u79FB\u9664\u8BE5\u914D\u7F6E\uFF08\u751F\u4EA7\u5FC5\u987B\u7528 HTTPS\uFF09\u3002"
|
|
63
|
+
);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
function getHostCookieOptions(maxAge, secure = true) {
|
|
50
67
|
return {
|
|
51
68
|
httpOnly: true,
|
|
52
|
-
secure
|
|
69
|
+
secure,
|
|
53
70
|
sameSite: "lax",
|
|
54
71
|
path: "/",
|
|
55
72
|
...maxAge !== void 0 ? { maxAge } : {}
|
|
56
73
|
};
|
|
57
74
|
}
|
|
58
|
-
function getSecureCookieOptions(maxAge, path = "/") {
|
|
75
|
+
function getSecureCookieOptions(maxAge, path = "/", secure = true) {
|
|
59
76
|
return {
|
|
60
77
|
httpOnly: true,
|
|
61
|
-
secure
|
|
78
|
+
secure,
|
|
62
79
|
sameSite: "lax",
|
|
63
80
|
path,
|
|
64
81
|
...maxAge !== void 0 ? { maxAge } : {}
|
|
@@ -176,11 +193,14 @@ function createSsoMiddleware(config) {
|
|
|
176
193
|
callbackPath = "/api/auth/callback",
|
|
177
194
|
ssoCookieName = "__Host-user_token",
|
|
178
195
|
validateSsoCookie = true,
|
|
179
|
-
|
|
180
|
-
stateCookieName = DEFAULT_STATE_COOKIE_NAME,
|
|
181
|
-
returnUrlCookieName = DEFAULT_RETURN_COOKIE_NAME,
|
|
182
|
-
verifierCookieName = DEFAULT_VERIFIER_COOKIE_NAME
|
|
196
|
+
insecureLocalDev: insecureLocalDevOpt = false
|
|
183
197
|
} = config;
|
|
198
|
+
const insecureLocalDev = resolveInsecureLocalDev(insecureLocalDevOpt, ssoBaseUrl);
|
|
199
|
+
const secureCookies = !insecureLocalDev;
|
|
200
|
+
const accessTokenCookieName = insecureLocalDev ? toInsecureCookieName(config.accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE_NAME) : config.accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE_NAME;
|
|
201
|
+
const stateCookieName = insecureLocalDev ? toInsecureCookieName(config.stateCookieName ?? DEFAULT_STATE_COOKIE_NAME) : config.stateCookieName ?? DEFAULT_STATE_COOKIE_NAME;
|
|
202
|
+
const returnUrlCookieName = insecureLocalDev ? toInsecureCookieName(config.returnUrlCookieName ?? DEFAULT_RETURN_COOKIE_NAME) : config.returnUrlCookieName ?? DEFAULT_RETURN_COOKIE_NAME;
|
|
203
|
+
const verifierCookieName = insecureLocalDev ? toInsecureCookieName(config.verifierCookieName ?? DEFAULT_VERIFIER_COOKIE_NAME) : config.verifierCookieName ?? DEFAULT_VERIFIER_COOKIE_NAME;
|
|
184
204
|
const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
|
|
185
205
|
if (process.env.NODE_ENV !== "production") {
|
|
186
206
|
if (!validateSsoCookie) {
|
|
@@ -248,12 +268,12 @@ function createSsoMiddleware(config) {
|
|
|
248
268
|
const loginUrl = new URL("/api/oauth/authorize", normalizedBase);
|
|
249
269
|
loginUrl.search = authorizeParams.toString();
|
|
250
270
|
const response = import_server.NextResponse.redirect(loginUrl);
|
|
251
|
-
response.cookies.set(stateCookieName, state, getHostCookieOptions(600));
|
|
252
|
-
response.cookies.set(verifierCookieName, verifier, getSecureCookieOptions(600, callbackPath));
|
|
271
|
+
response.cookies.set(stateCookieName, state, getHostCookieOptions(600, secureCookies));
|
|
272
|
+
response.cookies.set(verifierCookieName, verifier, getSecureCookieOptions(600, callbackPath, secureCookies));
|
|
253
273
|
const safeReturnUrl = (request.nextUrl.pathname + request.nextUrl.search).slice(0, 2048);
|
|
254
|
-
response.cookies.set(returnUrlCookieName, safeReturnUrl, getHostCookieOptions(600));
|
|
274
|
+
response.cookies.set(returnUrlCookieName, safeReturnUrl, getHostCookieOptions(600, secureCookies));
|
|
255
275
|
if (accessTokenCookie?.value) {
|
|
256
|
-
response.cookies.set(accessTokenCookieName, "", getHostCookieOptions(0));
|
|
276
|
+
response.cookies.set(accessTokenCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
257
277
|
}
|
|
258
278
|
return response;
|
|
259
279
|
};
|
|
@@ -276,6 +296,7 @@ var SsoError = class extends Error {
|
|
|
276
296
|
// src/core/security.ts
|
|
277
297
|
function isTrustedReturnUrl(url, currentOrigin) {
|
|
278
298
|
if (!url) return false;
|
|
299
|
+
if (url.includes("\\")) return false;
|
|
279
300
|
if (url.startsWith("/") && !url.startsWith("//")) return true;
|
|
280
301
|
try {
|
|
281
302
|
return new URL(url).origin === currentOrigin;
|
|
@@ -481,7 +502,7 @@ async function validateIdToken(idToken, accessToken, expectedIssuer, expectedCli
|
|
|
481
502
|
if (typeof payload.exp !== "number") {
|
|
482
503
|
throw new SsoError("id_token_invalid", "ID Token \u7F3A\u5C11 exp \u58F0\u660E");
|
|
483
504
|
}
|
|
484
|
-
if (Date.now() >= payload.exp * 1e3) {
|
|
505
|
+
if (Date.now() >= payload.exp * 1e3 + 6e4) {
|
|
485
506
|
throw new SsoError("id_token_expired", "ID Token \u5DF2\u8FC7\u671F");
|
|
486
507
|
}
|
|
487
508
|
if (typeof payload.iat === "number" && payload.iat * 1e3 > Date.now() + 6e4) {
|
|
@@ -507,13 +528,17 @@ function createCallbackRouteHandler(config) {
|
|
|
507
528
|
redirectUri,
|
|
508
529
|
clientSecret,
|
|
509
530
|
defaultReturnPath = "/",
|
|
510
|
-
|
|
511
|
-
refreshTokenCookieName = DEFAULT_REFRESH_TOKEN_COOKIE_NAME,
|
|
512
|
-
idTokenCookieName = DEFAULT_ID_TOKEN_COOKIE_NAME,
|
|
513
|
-
stateCookieName = DEFAULT_STATE_COOKIE_NAME,
|
|
514
|
-
returnUrlCookieName = DEFAULT_RETURN_COOKIE_NAME,
|
|
515
|
-
verifierCookieName = DEFAULT_VERIFIER_COOKIE_NAME
|
|
531
|
+
insecureLocalDev: insecureLocalDevOpt = false
|
|
516
532
|
} = config;
|
|
533
|
+
const insecureLocalDev = resolveInsecureLocalDev(insecureLocalDevOpt, ssoBaseUrl);
|
|
534
|
+
const secureCookies = !insecureLocalDev;
|
|
535
|
+
const pickName = (explicit, fallback) => insecureLocalDev ? toInsecureCookieName(explicit ?? fallback) : explicit ?? fallback;
|
|
536
|
+
const accessTokenCookieName = pickName(config.accessTokenCookieName, DEFAULT_ACCESS_TOKEN_COOKIE_NAME);
|
|
537
|
+
const refreshTokenCookieName = pickName(config.refreshTokenCookieName, DEFAULT_REFRESH_TOKEN_COOKIE_NAME);
|
|
538
|
+
const idTokenCookieName = pickName(config.idTokenCookieName, DEFAULT_ID_TOKEN_COOKIE_NAME);
|
|
539
|
+
const stateCookieName = pickName(config.stateCookieName, DEFAULT_STATE_COOKIE_NAME);
|
|
540
|
+
const returnUrlCookieName = pickName(config.returnUrlCookieName, DEFAULT_RETURN_COOKIE_NAME);
|
|
541
|
+
const verifierCookieName = pickName(config.verifierCookieName, DEFAULT_VERIFIER_COOKIE_NAME);
|
|
517
542
|
const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
|
|
518
543
|
return async function GET(request) {
|
|
519
544
|
const { searchParams } = request.nextUrl;
|
|
@@ -621,6 +646,15 @@ function createCallbackRouteHandler(config) {
|
|
|
621
646
|
);
|
|
622
647
|
}
|
|
623
648
|
const tokenData = await res.json();
|
|
649
|
+
if (!tokenData.access_token || !tokenData.refresh_token) {
|
|
650
|
+
return import_server2.NextResponse.json(
|
|
651
|
+
{
|
|
652
|
+
error: "server_error",
|
|
653
|
+
error_description: "Token \u54CD\u5E94\u7F3A\u5C11 access_token \u6216 refresh_token"
|
|
654
|
+
},
|
|
655
|
+
{ status: 502 }
|
|
656
|
+
);
|
|
657
|
+
}
|
|
624
658
|
if (tokenData.id_token) {
|
|
625
659
|
try {
|
|
626
660
|
await validateIdToken(
|
|
@@ -639,15 +673,16 @@ function createCallbackRouteHandler(config) {
|
|
|
639
673
|
);
|
|
640
674
|
}
|
|
641
675
|
}
|
|
676
|
+
const callbackOrigin = new URL(redirectUri).origin;
|
|
642
677
|
const rawReturnUrl = request.cookies.get(returnUrlCookieName)?.value || defaultReturnPath;
|
|
643
|
-
const returnUrl = isTrustedReturnUrl(rawReturnUrl,
|
|
644
|
-
const response = import_server2.NextResponse.redirect(new URL(returnUrl,
|
|
678
|
+
const returnUrl = isTrustedReturnUrl(rawReturnUrl, callbackOrigin) ? rawReturnUrl : "/";
|
|
679
|
+
const response = import_server2.NextResponse.redirect(new URL(returnUrl, callbackOrigin));
|
|
645
680
|
response.cookies.set(accessTokenCookieName, tokenData.access_token, {
|
|
646
|
-
...getHostCookieOptions(tokenData.expires_in)
|
|
681
|
+
...getHostCookieOptions(tokenData.expires_in, secureCookies)
|
|
647
682
|
});
|
|
648
683
|
const refreshMaxAge = tokenData.refresh_expires_in != null ? tokenData.refresh_expires_in : 30 * 24 * 60 * 60;
|
|
649
684
|
response.cookies.set(refreshTokenCookieName, tokenData.refresh_token, {
|
|
650
|
-
...getHostCookieOptions(refreshMaxAge)
|
|
685
|
+
...getHostCookieOptions(refreshMaxAge, secureCookies)
|
|
651
686
|
});
|
|
652
687
|
if (tokenData.id_token) {
|
|
653
688
|
let idTokenMaxAge = 3600;
|
|
@@ -665,14 +700,14 @@ function createCallbackRouteHandler(config) {
|
|
|
665
700
|
}
|
|
666
701
|
if (idTokenMaxAge > 0) {
|
|
667
702
|
response.cookies.set(idTokenCookieName, tokenData.id_token, {
|
|
668
|
-
...getHostCookieOptions(idTokenMaxAge)
|
|
703
|
+
...getHostCookieOptions(idTokenMaxAge, secureCookies)
|
|
669
704
|
});
|
|
670
705
|
}
|
|
671
706
|
}
|
|
672
|
-
response.cookies.set(stateCookieName, "", getHostCookieOptions(0));
|
|
673
|
-
response.cookies.set(returnUrlCookieName, "", getHostCookieOptions(0));
|
|
674
|
-
response.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, "/"));
|
|
675
|
-
response.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, request.nextUrl.pathname));
|
|
707
|
+
response.cookies.set(stateCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
708
|
+
response.cookies.set(returnUrlCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
709
|
+
response.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, "/", secureCookies));
|
|
710
|
+
response.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, request.nextUrl.pathname, secureCookies));
|
|
676
711
|
return response;
|
|
677
712
|
};
|
|
678
713
|
}
|
|
@@ -717,16 +752,21 @@ function createLogoutRouteHandler(config) {
|
|
|
717
752
|
clientSecret,
|
|
718
753
|
postLogoutRedirectUri = new URL(redirectUri).origin + "/",
|
|
719
754
|
redirectToSso = true,
|
|
720
|
-
accessTokenCookieName = DEFAULT_ACCESS_TOKEN_COOKIE_NAME,
|
|
721
|
-
refreshTokenCookieName = DEFAULT_REFRESH_TOKEN_COOKIE_NAME,
|
|
722
|
-
idTokenCookieName = DEFAULT_ID_TOKEN_COOKIE_NAME,
|
|
723
|
-
stateCookieName = DEFAULT_STATE_COOKIE_NAME,
|
|
724
|
-
returnUrlCookieName = DEFAULT_RETURN_COOKIE_NAME,
|
|
725
|
-
verifierCookieName = DEFAULT_VERIFIER_COOKIE_NAME,
|
|
726
755
|
callbackPath = "/api/auth/callback",
|
|
727
|
-
|
|
756
|
+
insecureLocalDev: insecureLocalDevOpt = false
|
|
728
757
|
} = config;
|
|
758
|
+
const insecureLocalDev = resolveInsecureLocalDev(insecureLocalDevOpt, ssoBaseUrl);
|
|
759
|
+
const secureCookies = !insecureLocalDev;
|
|
760
|
+
const pickName = (explicit, fallback) => insecureLocalDev ? toInsecureCookieName(explicit ?? fallback) : explicit ?? fallback;
|
|
761
|
+
const accessTokenCookieName = pickName(config.accessTokenCookieName, DEFAULT_ACCESS_TOKEN_COOKIE_NAME);
|
|
762
|
+
const refreshTokenCookieName = pickName(config.refreshTokenCookieName, DEFAULT_REFRESH_TOKEN_COOKIE_NAME);
|
|
763
|
+
const idTokenCookieName = pickName(config.idTokenCookieName, DEFAULT_ID_TOKEN_COOKIE_NAME);
|
|
764
|
+
const stateCookieName = pickName(config.stateCookieName, DEFAULT_STATE_COOKIE_NAME);
|
|
765
|
+
const returnUrlCookieName = pickName(config.returnUrlCookieName, DEFAULT_RETURN_COOKIE_NAME);
|
|
766
|
+
const verifierCookieName = pickName(config.verifierCookieName, DEFAULT_VERIFIER_COOKIE_NAME);
|
|
767
|
+
const logoutStateCookieName = pickName(config.logoutStateCookieName, DEFAULT_LOGOUT_STATE_COOKIE_NAME);
|
|
729
768
|
const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
|
|
769
|
+
const callbackOrigin = new URL(redirectUri).origin;
|
|
730
770
|
return async function handler(request) {
|
|
731
771
|
const returnedState = request.nextUrl.searchParams.get("state");
|
|
732
772
|
if (returnedState) {
|
|
@@ -737,8 +777,8 @@ function createLogoutRouteHandler(config) {
|
|
|
737
777
|
{ status: 400 }
|
|
738
778
|
);
|
|
739
779
|
}
|
|
740
|
-
const res = import_server3.NextResponse.redirect(
|
|
741
|
-
res.cookies.set(logoutStateCookieName, "", getHostCookieOptions(0));
|
|
780
|
+
const res = import_server3.NextResponse.redirect(callbackOrigin + "/");
|
|
781
|
+
res.cookies.set(logoutStateCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
742
782
|
return res;
|
|
743
783
|
}
|
|
744
784
|
const refreshToken = request.cookies.get(refreshTokenCookieName)?.value;
|
|
@@ -764,18 +804,18 @@ function createLogoutRouteHandler(config) {
|
|
|
764
804
|
}
|
|
765
805
|
}
|
|
766
806
|
const clearCookies = (res) => {
|
|
767
|
-
res.cookies.set(accessTokenCookieName, "", getHostCookieOptions(0));
|
|
768
|
-
res.cookies.set(refreshTokenCookieName, "", getHostCookieOptions(0));
|
|
769
|
-
res.cookies.set(idTokenCookieName, "", getHostCookieOptions(0));
|
|
770
|
-
res.cookies.set(stateCookieName, "", getHostCookieOptions(0));
|
|
771
|
-
res.cookies.set(returnUrlCookieName, "", getHostCookieOptions(0));
|
|
772
|
-
res.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, "/"));
|
|
773
|
-
res.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, callbackPath));
|
|
807
|
+
res.cookies.set(accessTokenCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
808
|
+
res.cookies.set(refreshTokenCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
809
|
+
res.cookies.set(idTokenCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
810
|
+
res.cookies.set(stateCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
811
|
+
res.cookies.set(returnUrlCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
812
|
+
res.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, "/", secureCookies));
|
|
813
|
+
res.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, callbackPath, secureCookies));
|
|
774
814
|
return res;
|
|
775
815
|
};
|
|
776
816
|
if (redirectToSso) {
|
|
777
817
|
const discovery = await fetchDiscovery(normalizedBase);
|
|
778
|
-
const endSessionEndpoint = discovery?.end_session_endpoint || `${normalizedBase}/
|
|
818
|
+
const endSessionEndpoint = discovery?.end_session_endpoint || `${normalizedBase}/api/oauth/end-session`;
|
|
779
819
|
const logoutUrl = new URL(endSessionEndpoint);
|
|
780
820
|
logoutUrl.searchParams.set("client_id", clientId);
|
|
781
821
|
logoutUrl.searchParams.set(
|
|
@@ -788,11 +828,11 @@ function createLogoutRouteHandler(config) {
|
|
|
788
828
|
const logoutState = generateRandomString2(32);
|
|
789
829
|
logoutUrl.searchParams.set("state", logoutState);
|
|
790
830
|
const res = clearCookies(import_server3.NextResponse.redirect(logoutUrl.toString()));
|
|
791
|
-
res.cookies.set(logoutStateCookieName, logoutState, getHostCookieOptions(600));
|
|
831
|
+
res.cookies.set(logoutStateCookieName, logoutState, getHostCookieOptions(600, secureCookies));
|
|
792
832
|
return res;
|
|
793
833
|
}
|
|
794
834
|
return clearCookies(
|
|
795
|
-
import_server3.NextResponse.redirect(
|
|
835
|
+
import_server3.NextResponse.redirect(callbackOrigin + "/")
|
|
796
836
|
);
|
|
797
837
|
};
|
|
798
838
|
}
|
|
@@ -809,6 +849,7 @@ function createLogoutRouteHandler(config) {
|
|
|
809
849
|
createLogoutRouteHandler,
|
|
810
850
|
createSsoMiddleware,
|
|
811
851
|
getHostCookieOptions,
|
|
812
|
-
getSecureCookieOptions
|
|
852
|
+
getSecureCookieOptions,
|
|
853
|
+
toInsecureCookieName
|
|
813
854
|
});
|
|
814
855
|
//# sourceMappingURL=index.js.map
|