@nihplod/sso-sdk 1.1.0 → 1.2.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 +73 -6
- package/dist/index.d.mts +46 -10
- package/dist/index.d.ts +46 -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 +84 -45
- package/dist/next/index.js.map +1 -1
- package/dist/next/index.mjs +82 -44
- package/dist/next/index.mjs.map +1 -1
- package/dist/react/index.d.mts +108 -10
- package/dist/react/index.d.ts +108 -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.mjs
CHANGED
|
@@ -9,19 +9,35 @@ var DEFAULT_STATE_COOKIE_NAME = "__Host-nihplod_sso_state";
|
|
|
9
9
|
var DEFAULT_RETURN_COOKIE_NAME = "__Host-nihplod_sso_return";
|
|
10
10
|
var DEFAULT_VERIFIER_COOKIE_NAME = "__Secure-nihplod_sso_verifier";
|
|
11
11
|
var DEFAULT_LOGOUT_STATE_COOKIE_NAME = "__Host-nihplod_sso_logout_state";
|
|
12
|
-
function
|
|
12
|
+
function toInsecureCookieName(name) {
|
|
13
|
+
return name.replace(/^__(Host|Secure)-/, "");
|
|
14
|
+
}
|
|
15
|
+
function resolveInsecureLocalDev(enabled, ssoBaseUrl) {
|
|
16
|
+
if (!enabled) return false;
|
|
17
|
+
if (process.env.NODE_ENV === "production" && /^https:\/\//i.test(ssoBaseUrl)) {
|
|
18
|
+
console.warn(
|
|
19
|
+
"[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"
|
|
20
|
+
);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
console.warn(
|
|
24
|
+
"[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"
|
|
25
|
+
);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
function getHostCookieOptions(maxAge, secure = true) {
|
|
13
29
|
return {
|
|
14
30
|
httpOnly: true,
|
|
15
|
-
secure
|
|
31
|
+
secure,
|
|
16
32
|
sameSite: "lax",
|
|
17
33
|
path: "/",
|
|
18
34
|
...maxAge !== void 0 ? { maxAge } : {}
|
|
19
35
|
};
|
|
20
36
|
}
|
|
21
|
-
function getSecureCookieOptions(maxAge, path = "/") {
|
|
37
|
+
function getSecureCookieOptions(maxAge, path = "/", secure = true) {
|
|
22
38
|
return {
|
|
23
39
|
httpOnly: true,
|
|
24
|
-
secure
|
|
40
|
+
secure,
|
|
25
41
|
sameSite: "lax",
|
|
26
42
|
path,
|
|
27
43
|
...maxAge !== void 0 ? { maxAge } : {}
|
|
@@ -139,11 +155,14 @@ function createSsoMiddleware(config) {
|
|
|
139
155
|
callbackPath = "/api/auth/callback",
|
|
140
156
|
ssoCookieName = "__Host-user_token",
|
|
141
157
|
validateSsoCookie = true,
|
|
142
|
-
|
|
143
|
-
stateCookieName = DEFAULT_STATE_COOKIE_NAME,
|
|
144
|
-
returnUrlCookieName = DEFAULT_RETURN_COOKIE_NAME,
|
|
145
|
-
verifierCookieName = DEFAULT_VERIFIER_COOKIE_NAME
|
|
158
|
+
insecureLocalDev: insecureLocalDevOpt = false
|
|
146
159
|
} = config;
|
|
160
|
+
const insecureLocalDev = resolveInsecureLocalDev(insecureLocalDevOpt, ssoBaseUrl);
|
|
161
|
+
const secureCookies = !insecureLocalDev;
|
|
162
|
+
const accessTokenCookieName = insecureLocalDev ? toInsecureCookieName(config.accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE_NAME) : config.accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE_NAME;
|
|
163
|
+
const stateCookieName = insecureLocalDev ? toInsecureCookieName(config.stateCookieName ?? DEFAULT_STATE_COOKIE_NAME) : config.stateCookieName ?? DEFAULT_STATE_COOKIE_NAME;
|
|
164
|
+
const returnUrlCookieName = insecureLocalDev ? toInsecureCookieName(config.returnUrlCookieName ?? DEFAULT_RETURN_COOKIE_NAME) : config.returnUrlCookieName ?? DEFAULT_RETURN_COOKIE_NAME;
|
|
165
|
+
const verifierCookieName = insecureLocalDev ? toInsecureCookieName(config.verifierCookieName ?? DEFAULT_VERIFIER_COOKIE_NAME) : config.verifierCookieName ?? DEFAULT_VERIFIER_COOKIE_NAME;
|
|
147
166
|
const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
|
|
148
167
|
if (process.env.NODE_ENV !== "production") {
|
|
149
168
|
if (!validateSsoCookie) {
|
|
@@ -211,12 +230,12 @@ function createSsoMiddleware(config) {
|
|
|
211
230
|
const loginUrl = new URL("/api/oauth/authorize", normalizedBase);
|
|
212
231
|
loginUrl.search = authorizeParams.toString();
|
|
213
232
|
const response = NextResponse.redirect(loginUrl);
|
|
214
|
-
response.cookies.set(stateCookieName, state, getHostCookieOptions(600));
|
|
215
|
-
response.cookies.set(verifierCookieName, verifier, getSecureCookieOptions(600, callbackPath));
|
|
233
|
+
response.cookies.set(stateCookieName, state, getHostCookieOptions(600, secureCookies));
|
|
234
|
+
response.cookies.set(verifierCookieName, verifier, getSecureCookieOptions(600, callbackPath, secureCookies));
|
|
216
235
|
const safeReturnUrl = (request.nextUrl.pathname + request.nextUrl.search).slice(0, 2048);
|
|
217
|
-
response.cookies.set(returnUrlCookieName, safeReturnUrl, getHostCookieOptions(600));
|
|
236
|
+
response.cookies.set(returnUrlCookieName, safeReturnUrl, getHostCookieOptions(600, secureCookies));
|
|
218
237
|
if (accessTokenCookie?.value) {
|
|
219
|
-
response.cookies.set(accessTokenCookieName, "", getHostCookieOptions(0));
|
|
238
|
+
response.cookies.set(accessTokenCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
220
239
|
}
|
|
221
240
|
return response;
|
|
222
241
|
};
|
|
@@ -239,6 +258,7 @@ var SsoError = class extends Error {
|
|
|
239
258
|
// src/core/security.ts
|
|
240
259
|
function isTrustedReturnUrl(url, currentOrigin) {
|
|
241
260
|
if (!url) return false;
|
|
261
|
+
if (url.includes("\\")) return false;
|
|
242
262
|
if (url.startsWith("/") && !url.startsWith("//")) return true;
|
|
243
263
|
try {
|
|
244
264
|
return new URL(url).origin === currentOrigin;
|
|
@@ -444,7 +464,7 @@ async function validateIdToken(idToken, accessToken, expectedIssuer, expectedCli
|
|
|
444
464
|
if (typeof payload.exp !== "number") {
|
|
445
465
|
throw new SsoError("id_token_invalid", "ID Token \u7F3A\u5C11 exp \u58F0\u660E");
|
|
446
466
|
}
|
|
447
|
-
if (Date.now() >= payload.exp * 1e3) {
|
|
467
|
+
if (Date.now() >= payload.exp * 1e3 + 6e4) {
|
|
448
468
|
throw new SsoError("id_token_expired", "ID Token \u5DF2\u8FC7\u671F");
|
|
449
469
|
}
|
|
450
470
|
if (typeof payload.iat === "number" && payload.iat * 1e3 > Date.now() + 6e4) {
|
|
@@ -470,13 +490,17 @@ function createCallbackRouteHandler(config) {
|
|
|
470
490
|
redirectUri,
|
|
471
491
|
clientSecret,
|
|
472
492
|
defaultReturnPath = "/",
|
|
473
|
-
|
|
474
|
-
refreshTokenCookieName = DEFAULT_REFRESH_TOKEN_COOKIE_NAME,
|
|
475
|
-
idTokenCookieName = DEFAULT_ID_TOKEN_COOKIE_NAME,
|
|
476
|
-
stateCookieName = DEFAULT_STATE_COOKIE_NAME,
|
|
477
|
-
returnUrlCookieName = DEFAULT_RETURN_COOKIE_NAME,
|
|
478
|
-
verifierCookieName = DEFAULT_VERIFIER_COOKIE_NAME
|
|
493
|
+
insecureLocalDev: insecureLocalDevOpt = false
|
|
479
494
|
} = config;
|
|
495
|
+
const insecureLocalDev = resolveInsecureLocalDev(insecureLocalDevOpt, ssoBaseUrl);
|
|
496
|
+
const secureCookies = !insecureLocalDev;
|
|
497
|
+
const pickName = (explicit, fallback) => insecureLocalDev ? toInsecureCookieName(explicit ?? fallback) : explicit ?? fallback;
|
|
498
|
+
const accessTokenCookieName = pickName(config.accessTokenCookieName, DEFAULT_ACCESS_TOKEN_COOKIE_NAME);
|
|
499
|
+
const refreshTokenCookieName = pickName(config.refreshTokenCookieName, DEFAULT_REFRESH_TOKEN_COOKIE_NAME);
|
|
500
|
+
const idTokenCookieName = pickName(config.idTokenCookieName, DEFAULT_ID_TOKEN_COOKIE_NAME);
|
|
501
|
+
const stateCookieName = pickName(config.stateCookieName, DEFAULT_STATE_COOKIE_NAME);
|
|
502
|
+
const returnUrlCookieName = pickName(config.returnUrlCookieName, DEFAULT_RETURN_COOKIE_NAME);
|
|
503
|
+
const verifierCookieName = pickName(config.verifierCookieName, DEFAULT_VERIFIER_COOKIE_NAME);
|
|
480
504
|
const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
|
|
481
505
|
return async function GET(request) {
|
|
482
506
|
const { searchParams } = request.nextUrl;
|
|
@@ -584,6 +608,15 @@ function createCallbackRouteHandler(config) {
|
|
|
584
608
|
);
|
|
585
609
|
}
|
|
586
610
|
const tokenData = await res.json();
|
|
611
|
+
if (!tokenData.access_token || !tokenData.refresh_token) {
|
|
612
|
+
return NextResponse2.json(
|
|
613
|
+
{
|
|
614
|
+
error: "server_error",
|
|
615
|
+
error_description: "Token \u54CD\u5E94\u7F3A\u5C11 access_token \u6216 refresh_token"
|
|
616
|
+
},
|
|
617
|
+
{ status: 502 }
|
|
618
|
+
);
|
|
619
|
+
}
|
|
587
620
|
if (tokenData.id_token) {
|
|
588
621
|
try {
|
|
589
622
|
await validateIdToken(
|
|
@@ -606,11 +639,11 @@ function createCallbackRouteHandler(config) {
|
|
|
606
639
|
const returnUrl = isTrustedReturnUrl(rawReturnUrl, request.nextUrl.origin) ? rawReturnUrl : "/";
|
|
607
640
|
const response = NextResponse2.redirect(new URL(returnUrl, request.url));
|
|
608
641
|
response.cookies.set(accessTokenCookieName, tokenData.access_token, {
|
|
609
|
-
...getHostCookieOptions(tokenData.expires_in)
|
|
642
|
+
...getHostCookieOptions(tokenData.expires_in, secureCookies)
|
|
610
643
|
});
|
|
611
644
|
const refreshMaxAge = tokenData.refresh_expires_in != null ? tokenData.refresh_expires_in : 30 * 24 * 60 * 60;
|
|
612
645
|
response.cookies.set(refreshTokenCookieName, tokenData.refresh_token, {
|
|
613
|
-
...getHostCookieOptions(refreshMaxAge)
|
|
646
|
+
...getHostCookieOptions(refreshMaxAge, secureCookies)
|
|
614
647
|
});
|
|
615
648
|
if (tokenData.id_token) {
|
|
616
649
|
let idTokenMaxAge = 3600;
|
|
@@ -628,14 +661,14 @@ function createCallbackRouteHandler(config) {
|
|
|
628
661
|
}
|
|
629
662
|
if (idTokenMaxAge > 0) {
|
|
630
663
|
response.cookies.set(idTokenCookieName, tokenData.id_token, {
|
|
631
|
-
...getHostCookieOptions(idTokenMaxAge)
|
|
664
|
+
...getHostCookieOptions(idTokenMaxAge, secureCookies)
|
|
632
665
|
});
|
|
633
666
|
}
|
|
634
667
|
}
|
|
635
|
-
response.cookies.set(stateCookieName, "", getHostCookieOptions(0));
|
|
636
|
-
response.cookies.set(returnUrlCookieName, "", getHostCookieOptions(0));
|
|
637
|
-
response.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, "/"));
|
|
638
|
-
response.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, request.nextUrl.pathname));
|
|
668
|
+
response.cookies.set(stateCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
669
|
+
response.cookies.set(returnUrlCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
670
|
+
response.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, "/", secureCookies));
|
|
671
|
+
response.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, request.nextUrl.pathname, secureCookies));
|
|
639
672
|
return response;
|
|
640
673
|
};
|
|
641
674
|
}
|
|
@@ -680,15 +713,19 @@ function createLogoutRouteHandler(config) {
|
|
|
680
713
|
clientSecret,
|
|
681
714
|
postLogoutRedirectUri = new URL(redirectUri).origin + "/",
|
|
682
715
|
redirectToSso = true,
|
|
683
|
-
accessTokenCookieName = DEFAULT_ACCESS_TOKEN_COOKIE_NAME,
|
|
684
|
-
refreshTokenCookieName = DEFAULT_REFRESH_TOKEN_COOKIE_NAME,
|
|
685
|
-
idTokenCookieName = DEFAULT_ID_TOKEN_COOKIE_NAME,
|
|
686
|
-
stateCookieName = DEFAULT_STATE_COOKIE_NAME,
|
|
687
|
-
returnUrlCookieName = DEFAULT_RETURN_COOKIE_NAME,
|
|
688
|
-
verifierCookieName = DEFAULT_VERIFIER_COOKIE_NAME,
|
|
689
716
|
callbackPath = "/api/auth/callback",
|
|
690
|
-
|
|
717
|
+
insecureLocalDev: insecureLocalDevOpt = false
|
|
691
718
|
} = config;
|
|
719
|
+
const insecureLocalDev = resolveInsecureLocalDev(insecureLocalDevOpt, ssoBaseUrl);
|
|
720
|
+
const secureCookies = !insecureLocalDev;
|
|
721
|
+
const pickName = (explicit, fallback) => insecureLocalDev ? toInsecureCookieName(explicit ?? fallback) : explicit ?? fallback;
|
|
722
|
+
const accessTokenCookieName = pickName(config.accessTokenCookieName, DEFAULT_ACCESS_TOKEN_COOKIE_NAME);
|
|
723
|
+
const refreshTokenCookieName = pickName(config.refreshTokenCookieName, DEFAULT_REFRESH_TOKEN_COOKIE_NAME);
|
|
724
|
+
const idTokenCookieName = pickName(config.idTokenCookieName, DEFAULT_ID_TOKEN_COOKIE_NAME);
|
|
725
|
+
const stateCookieName = pickName(config.stateCookieName, DEFAULT_STATE_COOKIE_NAME);
|
|
726
|
+
const returnUrlCookieName = pickName(config.returnUrlCookieName, DEFAULT_RETURN_COOKIE_NAME);
|
|
727
|
+
const verifierCookieName = pickName(config.verifierCookieName, DEFAULT_VERIFIER_COOKIE_NAME);
|
|
728
|
+
const logoutStateCookieName = pickName(config.logoutStateCookieName, DEFAULT_LOGOUT_STATE_COOKIE_NAME);
|
|
692
729
|
const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
|
|
693
730
|
return async function handler(request) {
|
|
694
731
|
const returnedState = request.nextUrl.searchParams.get("state");
|
|
@@ -701,7 +738,7 @@ function createLogoutRouteHandler(config) {
|
|
|
701
738
|
);
|
|
702
739
|
}
|
|
703
740
|
const res = NextResponse3.redirect(request.nextUrl.origin + "/");
|
|
704
|
-
res.cookies.set(logoutStateCookieName, "", getHostCookieOptions(0));
|
|
741
|
+
res.cookies.set(logoutStateCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
705
742
|
return res;
|
|
706
743
|
}
|
|
707
744
|
const refreshToken = request.cookies.get(refreshTokenCookieName)?.value;
|
|
@@ -727,18 +764,18 @@ function createLogoutRouteHandler(config) {
|
|
|
727
764
|
}
|
|
728
765
|
}
|
|
729
766
|
const clearCookies = (res) => {
|
|
730
|
-
res.cookies.set(accessTokenCookieName, "", getHostCookieOptions(0));
|
|
731
|
-
res.cookies.set(refreshTokenCookieName, "", getHostCookieOptions(0));
|
|
732
|
-
res.cookies.set(idTokenCookieName, "", getHostCookieOptions(0));
|
|
733
|
-
res.cookies.set(stateCookieName, "", getHostCookieOptions(0));
|
|
734
|
-
res.cookies.set(returnUrlCookieName, "", getHostCookieOptions(0));
|
|
735
|
-
res.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, "/"));
|
|
736
|
-
res.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, callbackPath));
|
|
767
|
+
res.cookies.set(accessTokenCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
768
|
+
res.cookies.set(refreshTokenCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
769
|
+
res.cookies.set(idTokenCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
770
|
+
res.cookies.set(stateCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
771
|
+
res.cookies.set(returnUrlCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
772
|
+
res.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, "/", secureCookies));
|
|
773
|
+
res.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, callbackPath, secureCookies));
|
|
737
774
|
return res;
|
|
738
775
|
};
|
|
739
776
|
if (redirectToSso) {
|
|
740
777
|
const discovery = await fetchDiscovery(normalizedBase);
|
|
741
|
-
const endSessionEndpoint = discovery?.end_session_endpoint || `${normalizedBase}/
|
|
778
|
+
const endSessionEndpoint = discovery?.end_session_endpoint || `${normalizedBase}/api/oauth/end-session`;
|
|
742
779
|
const logoutUrl = new URL(endSessionEndpoint);
|
|
743
780
|
logoutUrl.searchParams.set("client_id", clientId);
|
|
744
781
|
logoutUrl.searchParams.set(
|
|
@@ -751,7 +788,7 @@ function createLogoutRouteHandler(config) {
|
|
|
751
788
|
const logoutState = generateRandomString2(32);
|
|
752
789
|
logoutUrl.searchParams.set("state", logoutState);
|
|
753
790
|
const res = clearCookies(NextResponse3.redirect(logoutUrl.toString()));
|
|
754
|
-
res.cookies.set(logoutStateCookieName, logoutState, getHostCookieOptions(600));
|
|
791
|
+
res.cookies.set(logoutStateCookieName, logoutState, getHostCookieOptions(600, secureCookies));
|
|
755
792
|
return res;
|
|
756
793
|
}
|
|
757
794
|
return clearCookies(
|
|
@@ -771,6 +808,7 @@ export {
|
|
|
771
808
|
createLogoutRouteHandler,
|
|
772
809
|
createSsoMiddleware,
|
|
773
810
|
getHostCookieOptions,
|
|
774
|
-
getSecureCookieOptions
|
|
811
|
+
getSecureCookieOptions,
|
|
812
|
+
toInsecureCookieName
|
|
775
813
|
};
|
|
776
814
|
//# sourceMappingURL=index.mjs.map
|