@cosmicdrift/kumiko-server-runtime 0.200.1 → 0.202.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/package.json +3 -3
- package/src/run-prod-app-boot-context.ts +26 -1
- package/src/run-prod-app.ts +13 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-server-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.202.0",
|
|
4
4
|
"description": "Production server-boot runtime for Kumiko apps: connections, schema-drift-gate, seeds, lifecycle, graceful shutdown. Symmetric to kumiko-dev-server's runDevApp, without dev/scaffold/codegen tooling.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
84
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
83
|
+
"@cosmicdrift/kumiko-bundled-features": "0.202.0",
|
|
84
|
+
"@cosmicdrift/kumiko-framework": "0.202.0",
|
|
85
85
|
"temporal-polyfill": "^0.3.2"
|
|
86
86
|
},
|
|
87
87
|
"publishConfig": {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { makeAuthPaths } from "@cosmicdrift/kumiko-bundled-features/auth-email-password";
|
|
2
2
|
import { resolveSessionStore } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
bindMfaRevokeAllOtherSessionsFromFeature,
|
|
5
|
+
bindRevokeAllPatTokensFromFeature,
|
|
6
|
+
} from "@cosmicdrift/kumiko-bundled-features/auth-mfa";
|
|
4
7
|
import { createSmtpTransportFromEnv } from "@cosmicdrift/kumiko-bundled-features/channel-email";
|
|
5
8
|
import {
|
|
6
9
|
buildEnvConfigOverrides,
|
|
@@ -12,6 +15,10 @@ import {
|
|
|
12
15
|
createDeliveryService,
|
|
13
16
|
DELIVERY_FEATURE,
|
|
14
17
|
} from "@cosmicdrift/kumiko-bundled-features/delivery";
|
|
18
|
+
import {
|
|
19
|
+
bindPatAutoRevokeOnPasswordChangeFromFeature,
|
|
20
|
+
revokeAllPatTokensForUser,
|
|
21
|
+
} from "@cosmicdrift/kumiko-bundled-features/personal-access-tokens";
|
|
15
22
|
import {
|
|
16
23
|
createSecretsContext,
|
|
17
24
|
SECRETS_FEATURE_NAME,
|
|
@@ -215,6 +222,7 @@ export async function buildProdSessionAuth(
|
|
|
215
222
|
registry: Registry,
|
|
216
223
|
sessionsFeature: FeatureDefinition | undefined,
|
|
217
224
|
mfaFeature: FeatureDefinition | undefined,
|
|
225
|
+
patFeature: FeatureDefinition | undefined,
|
|
218
226
|
): Promise<{
|
|
219
227
|
readonly sessionCreator: Awaited<ReturnType<typeof resolveSessionStore>>["creator"];
|
|
220
228
|
readonly sessionRevoker: Awaited<ReturnType<typeof resolveSessionStore>>["revoker"];
|
|
@@ -228,9 +236,26 @@ export async function buildProdSessionAuth(
|
|
|
228
236
|
if (mfaFeature) {
|
|
229
237
|
bindMfaRevokeAllOtherSessionsFromFeature(mfaFeature)?.(store.revokeAllOthers);
|
|
230
238
|
}
|
|
239
|
+
if (mfaFeature && patFeature) {
|
|
240
|
+
bindRevokeAllPatTokensFromFeature(mfaFeature)?.((userId) =>
|
|
241
|
+
revokeAllPatTokensForUser(db, userId),
|
|
242
|
+
);
|
|
243
|
+
}
|
|
231
244
|
return {
|
|
232
245
|
sessionCreator: store.creator,
|
|
233
246
|
sessionRevoker: store.revoker,
|
|
234
247
|
sessionChecker: store.checker,
|
|
235
248
|
};
|
|
236
249
|
}
|
|
250
|
+
|
|
251
|
+
// Password-change → PAT-revoke, independent of sessions/MFA being mounted
|
|
252
|
+
// (unlike buildProdSessionAuth, which only runs when a sessionStore provider
|
|
253
|
+
// is registered). Call unconditionally whenever personal-access-tokens is
|
|
254
|
+
// mounted — a no-op if it isn't (bindPatAutoRevokeOnPasswordChangeFromFeature
|
|
255
|
+
// returns undefined for an unmounted feature.exports shape, but callers
|
|
256
|
+
// should still gate on patFeature being defined at all).
|
|
257
|
+
export function wireProdPatAutoRevoke(db: DbConnection, patFeature: FeatureDefinition): void {
|
|
258
|
+
bindPatAutoRevokeOnPasswordChangeFromFeature(patFeature)?.((userId) =>
|
|
259
|
+
revokeAllPatTokensForUser(db, userId),
|
|
260
|
+
);
|
|
261
|
+
}
|
package/src/run-prod-app.ts
CHANGED
|
@@ -148,6 +148,7 @@ import {
|
|
|
148
148
|
buildBootExtraContext,
|
|
149
149
|
buildProdSessionAuth,
|
|
150
150
|
resolveAuthMail,
|
|
151
|
+
wireProdPatAutoRevoke,
|
|
151
152
|
} from "./run-prod-app-boot-context";
|
|
152
153
|
import { buildStaticFallback } from "./run-prod-app-static-files";
|
|
153
154
|
import { type SecurityHeadersOption, withSecurityHeaders } from "./security-headers";
|
|
@@ -955,20 +956,30 @@ export async function runProdApp(options: RunProdAppOptions): Promise<ProdAppHan
|
|
|
955
956
|
// über die auth-routes.
|
|
956
957
|
// Secure-by-default (#1372): sessionStore provider → resolveSessionStore.
|
|
957
958
|
const mfaFeature = features.find((f) => f.name === AUTH_MFA_FEATURE);
|
|
959
|
+
// Hoisted above buildProdSessionAuth: that call also wires MFA-enable/
|
|
960
|
+
// disable → PAT-revoke (bindRevokeAllPatTokensFromFeature), so it needs
|
|
961
|
+
// patFeature too.
|
|
962
|
+
const patFeature = features.find((f) => f.name === PAT_FEATURE);
|
|
958
963
|
const sessionAuthFragment = shouldWireProdSessions(
|
|
959
964
|
Boolean(effectiveAuth),
|
|
960
965
|
registry.getExtensionUsages(EXT_SESSION_STORE).length > 0,
|
|
961
966
|
)
|
|
962
|
-
? await buildProdSessionAuth(db, registry, sessionsFeature, mfaFeature)
|
|
967
|
+
? await buildProdSessionAuth(db, registry, sessionsFeature, mfaFeature, patFeature)
|
|
963
968
|
: undefined;
|
|
964
969
|
|
|
970
|
+
// Password-change → PAT-revoke: independent of sessions/MFA being mounted
|
|
971
|
+
// (buildProdSessionAuth only runs when a sessionStore provider is
|
|
972
|
+
// registered), so wired unconditionally here whenever PAT itself is.
|
|
973
|
+
if (patFeature) {
|
|
974
|
+
wireProdPatAutoRevoke(db, patFeature);
|
|
975
|
+
}
|
|
976
|
+
|
|
965
977
|
// Token-verifier opt-in: any provider feature (personal-access-tokens, a
|
|
966
978
|
// future auth-provider-jwt, ...) self-registers via
|
|
967
979
|
// r.useExtension(EXT_TOKEN_VERIFIER, ...) — wire one generic resolver
|
|
968
980
|
// whenever at least one is mounted, resolved by shape at request-time.
|
|
969
981
|
// PAT keeps its own per-token rate limiter (patRateLimiter), unrelated to
|
|
970
982
|
// verification.
|
|
971
|
-
const patFeature = features.find((f) => f.name === PAT_FEATURE);
|
|
972
983
|
const hasTokenVerifierProviders = registry.getExtensionUsages(EXT_TOKEN_VERIFIER).length > 0;
|
|
973
984
|
let patAuthFragment:
|
|
974
985
|
| {
|