@nihplod/sso-sdk 1.4.0 → 1.4.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 +9 -0
- package/dist/next/index.js +18 -10
- package/dist/next/index.js.map +1 -1
- package/dist/next/index.mjs +18 -10
- package/dist/next/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -357,6 +357,15 @@ export const config = {
|
|
|
357
357
|
> code (silent re-auth), and the callback handler issues new cookies — the user typically
|
|
358
358
|
> only sees a quick redirect loop through the SSO center. No client-side action is needed.
|
|
359
359
|
|
|
360
|
+
> **Introspection outage behavior (since 1.4.1):** introspection requests time out after
|
|
361
|
+
> 5s. When introspection is *unreachable* (network error, timeout, or 5xx — as opposed to
|
|
362
|
+
> a confirmed-invalid token), the middleware fails open for requests that already carry an
|
|
363
|
+
> SSO `access_token` cookie: it lets the request through (with one `console.warn`) instead
|
|
364
|
+
> of redirecting to an SSO center that is likely down. This is safe because the middleware
|
|
365
|
+
> is only a UX gate — Route Handlers / Server Components must still re-verify the token.
|
|
366
|
+
> Confirmed-invalid tokens (401/403 or `active:false`) still trigger the redirect, and
|
|
367
|
+
> requests without any SSO cookie still redirect as before.
|
|
368
|
+
|
|
360
369
|
```typescript
|
|
361
370
|
// src/app/api/auth/callback/route.ts
|
|
362
371
|
import { createCallbackRouteHandler } from "@nihplod/sso-sdk/next";
|
package/dist/next/index.js
CHANGED
|
@@ -122,6 +122,7 @@ function matchesPath(pathname, paths) {
|
|
|
122
122
|
var introspectionCache = /* @__PURE__ */ new Map();
|
|
123
123
|
var INTROSPECT_CACHE_TTL_MS = 3e4;
|
|
124
124
|
var INTROSPECT_CACHE_MAX_ENTRIES = 500;
|
|
125
|
+
var INTROSPECT_TIMEOUT_MS = 5e3;
|
|
125
126
|
async function introspectCacheKey(token, clientId) {
|
|
126
127
|
const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(token));
|
|
127
128
|
const hex = Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
@@ -154,7 +155,7 @@ async function introspectAccessToken(token, ssoBaseUrl, clientId, clientSecret)
|
|
|
154
155
|
const cacheKey = await introspectCacheKey(token, clientId);
|
|
155
156
|
const cached = introspectCacheGet(cacheKey);
|
|
156
157
|
if (cached) {
|
|
157
|
-
return cached.active;
|
|
158
|
+
return cached.active ? "active" : "inactive";
|
|
158
159
|
}
|
|
159
160
|
try {
|
|
160
161
|
const body = new URLSearchParams({
|
|
@@ -168,21 +169,22 @@ async function introspectAccessToken(token, ssoBaseUrl, clientId, clientSecret)
|
|
|
168
169
|
const res = await fetch(`${ssoBaseUrl}/api/oauth/introspect`, {
|
|
169
170
|
method: "POST",
|
|
170
171
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
171
|
-
body: body.toString()
|
|
172
|
+
body: body.toString(),
|
|
173
|
+
signal: AbortSignal.timeout(INTROSPECT_TIMEOUT_MS)
|
|
172
174
|
});
|
|
173
175
|
if (!res.ok) {
|
|
174
176
|
if (res.status !== 401 && res.status !== 403) {
|
|
175
|
-
return
|
|
177
|
+
return "unreachable";
|
|
176
178
|
}
|
|
177
179
|
introspectCacheSet(cacheKey, false);
|
|
178
|
-
return
|
|
180
|
+
return "inactive";
|
|
179
181
|
}
|
|
180
182
|
const data = await res.json();
|
|
181
183
|
const active = data.active === true;
|
|
182
184
|
introspectCacheSet(cacheKey, active);
|
|
183
|
-
return active;
|
|
185
|
+
return active ? "active" : "inactive";
|
|
184
186
|
} catch {
|
|
185
|
-
return
|
|
187
|
+
return "unreachable";
|
|
186
188
|
}
|
|
187
189
|
}
|
|
188
190
|
function createSsoMiddleware(config) {
|
|
@@ -232,13 +234,13 @@ function createSsoMiddleware(config) {
|
|
|
232
234
|
const ssoSession = request.cookies.get(ssoCookieName);
|
|
233
235
|
if (ssoSession?.value) {
|
|
234
236
|
if (validateSsoCookie) {
|
|
235
|
-
const
|
|
237
|
+
const introspectResult = await introspectAccessToken(
|
|
236
238
|
ssoSession.value,
|
|
237
239
|
normalizedServerBase,
|
|
238
240
|
clientId,
|
|
239
241
|
clientSecret
|
|
240
242
|
);
|
|
241
|
-
if (
|
|
243
|
+
if (introspectResult === "active") {
|
|
242
244
|
return import_server.NextResponse.next();
|
|
243
245
|
}
|
|
244
246
|
} else {
|
|
@@ -247,13 +249,19 @@ function createSsoMiddleware(config) {
|
|
|
247
249
|
}
|
|
248
250
|
const accessTokenCookie = request.cookies.get(accessTokenCookieName);
|
|
249
251
|
if (accessTokenCookie?.value) {
|
|
250
|
-
const
|
|
252
|
+
const introspectResult = await introspectAccessToken(
|
|
251
253
|
accessTokenCookie.value,
|
|
252
254
|
normalizedServerBase,
|
|
253
255
|
clientId,
|
|
254
256
|
clientSecret
|
|
255
257
|
);
|
|
256
|
-
if (
|
|
258
|
+
if (introspectResult === "active") {
|
|
259
|
+
return import_server.NextResponse.next();
|
|
260
|
+
}
|
|
261
|
+
if (introspectResult === "unreachable") {
|
|
262
|
+
console.warn(
|
|
263
|
+
"[SSO SDK] introspection \u4E0D\u53EF\u8FBE\uFF08\u7F51\u7EDC\u5F02\u5E38/\u8D85\u65F6/5xx\uFF09\uFF0C\u5BF9\u6301\u6709 SSO access token Cookie \u7684\u8BF7\u6C42 fail-open \u653E\u884C\uFF1B\u654F\u611F\u6570\u636E\u7684\u9274\u6743\u7531 Route Handler \u515C\u5E95\u3002"
|
|
264
|
+
);
|
|
257
265
|
return import_server.NextResponse.next();
|
|
258
266
|
}
|
|
259
267
|
}
|