lambder 7.3.1 → 8.1.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/CHANGELOG.md +1047 -3
- package/README.md +46 -21
- package/dist/api/LambderApiAnswer.d.ts +18 -22
- package/dist/api/LambderApiAnswer.js +6 -7
- package/dist/api/LambderApiCallContext.d.ts +21 -8
- package/dist/api/LambderApiCallContext.js +22 -4
- package/dist/api/LambderApiDefinition.d.ts +4 -3
- package/dist/api/LambderApiEnvelope.d.ts +14 -9
- package/dist/api/LambderApiEnvelope.js +33 -34
- package/dist/api/LambderApiGuards.d.ts +78 -51
- package/dist/api/LambderApiGuards.js +34 -36
- package/dist/api/LambderApiIdempotency.d.ts +68 -62
- package/dist/api/LambderApiIdempotency.js +214 -151
- package/dist/api/LambderApiOutputValidationError.d.ts +32 -0
- package/dist/api/LambderApiOutputValidationError.js +50 -0
- package/dist/api/LambderApiPipeline.d.ts +47 -38
- package/dist/api/LambderApiPipeline.js +122 -63
- package/dist/api/LambderApiRateLimits.d.ts +201 -54
- package/dist/api/LambderApiRateLimits.js +185 -108
- package/dist/api/LambderApiRequest.d.ts +27 -21
- package/dist/api/LambderApiRequest.js +26 -19
- package/dist/api/LambderApiSignature.d.ts +12 -15
- package/dist/api/LambderApiSignature.js +28 -51
- package/dist/api/LambderApiValidationRefusal.d.ts +9 -9
- package/dist/api/LambderApiValidationRefusal.js +10 -10
- package/dist/build/ContractTypePrinter.d.ts +85 -0
- package/dist/build/ContractTypePrinter.js +402 -0
- package/dist/build/freshProcessVerifier.d.ts +13 -0
- package/dist/build/freshProcessVerifier.js +19 -0
- package/dist/build/moduleLocation.d.ts +11 -0
- package/dist/build/moduleLocation.js +6 -0
- package/dist/build/writeApiContract.d.ts +78 -0
- package/dist/build/writeApiContract.js +302 -0
- package/dist/build/writeApiSignatures.d.ts +114 -0
- package/dist/build/writeApiSignatures.js +217 -0
- package/dist/build/writeFileAtomically.d.ts +8 -0
- package/dist/build/writeFileAtomically.js +22 -0
- package/dist/build.d.ts +14 -0
- package/dist/build.js +11 -0
- package/dist/client/LambderCaller.d.ts +13 -44
- package/dist/client/LambderCaller.js +77 -84
- package/dist/client/LambderReloadLoopBreaker.d.ts +56 -26
- package/dist/client/LambderReloadLoopBreaker.js +90 -46
- package/dist/client/LambderUploadRunner.d.ts +96 -0
- package/dist/client/LambderUploadRunner.js +234 -0
- package/dist/client/lambderFetchTransport.d.ts +4 -1
- package/dist/client/lambderFetchTransport.js +52 -28
- package/dist/client.d.ts +9 -3
- package/dist/client.js +6 -1
- package/dist/core/Lambder.d.ts +143 -79
- package/dist/core/Lambder.js +350 -231
- package/dist/core/LambderContext.d.ts +82 -15
- package/dist/core/LambderContext.js +107 -20
- package/dist/core/LambderCors.d.ts +21 -3
- package/dist/core/LambderCors.js +35 -16
- package/dist/core/LambderCrashHandling.d.ts +40 -0
- package/dist/core/LambderCrashHandling.js +97 -0
- package/dist/core/LambderCreateOptions.d.ts +151 -75
- package/dist/core/LambderCreateOptions.js +16 -23
- package/dist/core/LambderFiles.d.ts +21 -7
- package/dist/core/LambderFiles.js +62 -34
- package/dist/core/LambderIndexHtml.js +12 -11
- package/dist/core/LambderPolicyBuilders.d.ts +17 -5
- package/dist/core/LambderPolicyBuilders.js +17 -5
- package/dist/core/LambderPublicFiles.d.ts +11 -5
- package/dist/core/LambderPublicFiles.js +32 -4
- package/dist/core/LambderRequestPath.d.ts +43 -0
- package/dist/core/LambderRequestPath.js +63 -0
- package/dist/core/LambderResponse.d.ts +26 -5
- package/dist/core/LambderResponse.js +157 -70
- package/dist/core/LambderResponseBuilder.d.ts +49 -4
- package/dist/core/LambderResponseBuilder.js +64 -3
- package/dist/core/LambderRouting.d.ts +2 -3
- package/dist/core/LambderRouting.js +22 -7
- package/dist/core/LambderTemplatingEngine.js +211 -32
- package/dist/index.d.ts +25 -8
- package/dist/index.js +13 -4
- package/dist/invoke/LambderInvokeCaller.d.ts +37 -42
- package/dist/invoke/LambderInvokeCaller.js +76 -66
- package/dist/invoke/LambderInvokeOutcome.d.ts +27 -26
- package/dist/invoke/LambderInvokeOutcome.js +9 -22
- package/dist/invoke/LambderLambdaEvent.d.ts +29 -9
- package/dist/invoke/LambderLambdaEvent.js +40 -22
- package/dist/invoke/lambderHandlerTransport.d.ts +9 -10
- package/dist/invoke/lambderHandlerTransport.js +15 -18
- package/dist/mock/LambderMockApp.d.ts +67 -83
- package/dist/mock/LambderMockApp.js +167 -153
- package/dist/mock/LambderMockBrowserCookies.d.ts +24 -28
- package/dist/mock/LambderMockBrowserCookies.js +24 -28
- package/dist/mock/LambderMockCallRecorder.d.ts +15 -22
- package/dist/mock/LambderMockCallRecorder.js +19 -28
- package/dist/mock/LambderMockCreateOptions.d.ts +42 -24
- package/dist/mock/LambderMockEntryRegistry.d.ts +11 -12
- package/dist/mock/LambderMockEntryRegistry.js +24 -29
- package/dist/mock/LambderMockFailureInjector.d.ts +3 -6
- package/dist/mock/LambderMockFailureInjector.js +3 -6
- package/dist/mock/LambderMockTypes.d.ts +78 -108
- package/dist/mock/lambderMockInvokeTransport.d.ts +11 -13
- package/dist/mock/lambderMockInvokeTransport.js +11 -10
- package/dist/mock/lambderMockMswHandler.d.ts +43 -33
- package/dist/mock/lambderMockMswHandler.js +50 -39
- package/dist/mock/lambderMockUploadMswHandler.d.ts +26 -0
- package/dist/mock/lambderMockUploadMswHandler.js +28 -0
- package/dist/mock.d.ts +4 -1
- package/dist/mock.js +6 -3
- package/dist/session/LambderSessionController.d.ts +108 -89
- package/dist/session/LambderSessionController.js +187 -168
- package/dist/session/LambderSessionCrypto.d.ts +16 -7
- package/dist/session/LambderSessionCrypto.js +26 -12
- package/dist/session/LambderSessionManager.d.ts +124 -46
- package/dist/session/LambderSessionManager.js +262 -137
- package/dist/shared/LambderHtml.d.ts +42 -3
- package/dist/shared/LambderHtml.js +127 -7
- package/dist/shared/LambderHtmlPositions.d.ts +173 -0
- package/dist/shared/LambderHtmlPositions.js +652 -0
- package/dist/shared/LambderI18n.d.ts +10 -11
- package/dist/shared/LambderI18n.js +33 -21
- package/dist/shared/contracts/LambderCache.d.ts +66 -0
- package/dist/shared/contracts/LambderCache.js +11 -0
- package/dist/shared/contracts/LambderFileSource.d.ts +6 -6
- package/dist/shared/contracts/LambderFileSource.js +5 -8
- package/dist/shared/contracts/LambderIdempotencyStore.d.ts +51 -22
- package/dist/shared/contracts/LambderIdempotencyStore.js +4 -5
- package/dist/shared/contracts/LambderRateLimiter.d.ts +27 -15
- package/dist/shared/contracts/LambderRateLimiter.js +4 -5
- package/dist/shared/contracts/LambderSessionStore.d.ts +65 -26
- package/dist/shared/contracts/LambderSessionStore.js +5 -6
- package/dist/shared/contracts/LambderUploadBucket.d.ts +154 -0
- package/dist/shared/contracts/LambderUploadBucket.js +74 -0
- package/dist/shared/transport/LambderApiTransport.d.ts +27 -27
- package/dist/shared/transport/LambderApiTransport.js +7 -7
- package/dist/shared/transport/LambderCookieJar.d.ts +28 -35
- package/dist/shared/transport/LambderCookieJar.js +54 -66
- package/dist/shared/transport/lambderCookieJarTransport.d.ts +11 -13
- package/dist/shared/transport/lambderCookieJarTransport.js +24 -23
- package/dist/shared/util/LambderCallAbort.d.ts +5 -5
- package/dist/shared/util/LambderCallAbort.js +5 -5
- package/dist/shared/util/LambderClientIp.d.ts +27 -11
- package/dist/shared/util/LambderClientIp.js +96 -13
- package/dist/shared/util/LambderContentDisposition.d.ts +10 -0
- package/dist/shared/util/LambderContentDisposition.js +13 -0
- package/dist/shared/util/LambderExpiringMap.d.ts +35 -49
- package/dist/shared/util/LambderExpiringMap.js +41 -57
- package/dist/shared/util/LambderNodeModules.js +6 -7
- package/dist/shared/util/LambderOptionChecks.d.ts +4 -4
- package/dist/shared/util/LambderOptionChecks.js +4 -4
- package/dist/shared/util/LambderResponseBrand.d.ts +5 -5
- package/dist/shared/util/LambderResponseBrand.js +5 -5
- package/dist/shared/util/LambderTextDigest.d.ts +7 -5
- package/dist/shared/util/LambderTextDigest.js +11 -5
- package/dist/shared/util/LambderTypeUtilities.d.ts +7 -8
- package/dist/shared/util/LambderTypeUtilities.js +3 -3
- package/dist/shared/util/boundKeyField.d.ts +20 -0
- package/dist/shared/util/boundKeyField.js +34 -0
- package/dist/shared/util/canonicalJson.d.ts +11 -0
- package/dist/shared/util/canonicalJson.js +28 -0
- package/dist/shared/util/joinKeyFields.d.ts +20 -0
- package/dist/shared/util/joinKeyFields.js +22 -0
- package/dist/shared/wire/LambderAnswerHeaders.d.ts +12 -16
- package/dist/shared/wire/LambderAnswerHeaders.js +12 -16
- package/dist/shared/wire/LambderApiContract.d.ts +98 -53
- package/dist/shared/wire/LambderApiOutcome.d.ts +43 -31
- package/dist/shared/wire/LambderApiOutcome.js +48 -23
- package/dist/shared/wire/LambderApiRefusal.d.ts +45 -27
- package/dist/shared/wire/LambderApiRefusal.js +42 -7
- package/dist/shared/wire/LambderApiSignature.d.ts +18 -22
- package/dist/shared/wire/LambderApiSignature.js +16 -19
- package/dist/shared/wire/LambderCallOptions.d.ts +38 -47
- package/dist/shared/wire/LambderCallOptions.js +9 -11
- package/dist/shared/wire/LambderCompressionCodec.d.ts +29 -34
- package/dist/shared/wire/LambderCompressionCodec.js +31 -36
- package/dist/shared/wire/LambderCompressionOption.d.ts +9 -9
- package/dist/shared/wire/LambderCompressionOption.js +9 -9
- package/dist/shared/wire/LambderCrashDetail.d.ts +12 -15
- package/dist/shared/wire/LambderCrashDetail.js +12 -15
- package/dist/shared/wire/LambderDefaultApiPath.d.ts +6 -0
- package/dist/shared/wire/LambderDefaultApiPath.js +6 -0
- package/dist/shared/wire/LambderHttpStatus.d.ts +6 -7
- package/dist/shared/wire/LambderIdempotencyKeyScope.d.ts +89 -0
- package/dist/shared/wire/LambderIdempotencyKeyScope.js +146 -0
- package/dist/shared/wire/LambderInvokeApiId.d.ts +27 -0
- package/dist/shared/wire/LambderInvokeApiId.js +27 -0
- package/dist/shared/wire/LambderOutcomeAssertions.d.ts +6 -7
- package/dist/shared/wire/LambderOutcomeAssertions.js +6 -7
- package/dist/shared/wire/LambderRequestPayload.d.ts +18 -20
- package/dist/shared/wire/LambderRequestPayload.js +4 -6
- package/dist/shared/wire/LambderUploadObjectFields.d.ts +10 -0
- package/dist/shared/wire/LambderUploadObjectFields.js +24 -0
- package/dist/shared/wire/LambderUploadRefusal.d.ts +9 -0
- package/dist/shared/wire/LambderUploadRefusal.js +18 -0
- package/dist/shared/wire/LambderUploadSchemas.d.ts +12 -0
- package/dist/shared/wire/LambderUploadSchemas.js +30 -0
- package/dist/stores/LambderCacheFiller.d.ts +48 -0
- package/dist/stores/LambderCacheFiller.js +119 -0
- package/dist/stores/LambderCacheKeys.d.ts +26 -0
- package/dist/stores/LambderCacheKeys.js +54 -0
- package/dist/stores/LambderCacheValues.d.ts +45 -0
- package/dist/stores/LambderCacheValues.js +74 -0
- package/dist/stores/LambderDdbCache.d.ts +121 -56
- package/dist/stores/LambderDdbCache.js +528 -225
- package/dist/stores/LambderDdbIdempotencyStore.d.ts +33 -22
- package/dist/stores/LambderDdbIdempotencyStore.js +75 -50
- package/dist/stores/LambderDdbRateLimiter.d.ts +76 -20
- package/dist/stores/LambderDdbRateLimiter.js +151 -39
- package/dist/stores/LambderDdbSdk.d.ts +43 -31
- package/dist/stores/LambderDdbSdk.js +80 -38
- package/dist/stores/LambderDdbSessionStore.d.ts +27 -14
- package/dist/stores/LambderDdbSessionStore.js +119 -47
- package/dist/stores/LambderHttpFileSource.d.ts +15 -6
- package/dist/stores/LambderHttpFileSource.js +15 -13
- package/dist/stores/LambderMemoryCache.d.ts +49 -0
- package/dist/stores/LambderMemoryCache.js +113 -0
- package/dist/stores/LambderMemoryIdempotencyStore.d.ts +13 -12
- package/dist/stores/LambderMemoryIdempotencyStore.js +31 -30
- package/dist/stores/LambderMemoryRateLimiter.d.ts +8 -9
- package/dist/stores/LambderMemoryRateLimiter.js +14 -13
- package/dist/stores/LambderMemorySessionStore.d.ts +14 -11
- package/dist/stores/LambderMemorySessionStore.js +38 -19
- package/dist/stores/LambderMemoryUploadBucket.d.ts +99 -0
- package/dist/stores/LambderMemoryUploadBucket.js +219 -0
- package/dist/stores/LambderS3FileSource.d.ts +21 -6
- package/dist/stores/LambderS3FileSource.js +12 -7
- package/dist/stores/LambderS3UploadBucket.d.ts +73 -0
- package/dist/stores/LambderS3UploadBucket.js +144 -0
- package/dist/stores/LambderSdkInstallHint.d.ts +11 -0
- package/dist/stores/LambderSdkInstallHint.js +14 -0
- package/dist/testing/LambderTestApp.d.ts +23 -25
- package/dist/testing/LambderTestApp.js +22 -24
- package/dist/testing/LambderTestVisitor.d.ts +10 -12
- package/dist/testing/LambderTestVisitor.js +15 -15
- package/dist/testing.d.ts +3 -0
- package/dist/testing.js +2 -0
- package/package.json +26 -3
- package/dist/api/LambderApiPolicyEngine.d.ts +0 -47
- package/dist/api/LambderApiPolicyEngine.js +0 -85
- package/dist/shared/util/LambderKeyFields.d.ts +0 -32
- package/dist/shared/util/LambderKeyFields.js +0 -34
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The cryptography the session model runs on, behind an interface so the
|
|
3
3
|
* manager itself has no Node dependency: the bearer secrets are hashed at
|
|
4
4
|
* rest, compared in constant time, and minted from a cryptographic random
|
|
5
|
-
* source.
|
|
5
|
+
* source, and the sessionKey is hashed under the salt as its key.
|
|
6
6
|
*
|
|
7
7
|
* LambderWebCrypto is the default and runs on Node 20+, every browser on a
|
|
8
8
|
* secure context, and edge runtimes. LambderPlainSessionCrypto is the
|
|
@@ -23,7 +23,7 @@ const constantTimeEqual = (a, b) => {
|
|
|
23
23
|
};
|
|
24
24
|
/** True when this runtime offers WebCrypto's subtle API (secure contexts in browsers; Node 20+). */
|
|
25
25
|
export const isWebCryptoAvailable = () => typeof globalThis.crypto?.subtle?.digest === "function" && typeof globalThis.crypto.getRandomValues === "function";
|
|
26
|
-
/** sha256 through crypto.subtle and randomness through getRandomValues: the default. */
|
|
26
|
+
/** sha256 and HMAC through crypto.subtle and randomness through getRandomValues: the default. */
|
|
27
27
|
export class LambderWebCrypto {
|
|
28
28
|
isCryptographic = true;
|
|
29
29
|
cryptoPromise;
|
|
@@ -37,11 +37,11 @@ export class LambderWebCrypto {
|
|
|
37
37
|
* The runtime's WebCrypto, through the resolver every layer shares, with
|
|
38
38
|
* Node's crypto warmed alongside it.
|
|
39
39
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* LambderPlainSessionCrypto and a memory store, which
|
|
44
|
-
*
|
|
40
|
+
* Availability is checked here, before the shared resolver, so the error
|
|
41
|
+
* can name this layer's own way out: a runtime with neither a global
|
|
42
|
+
* crypto nor Node's webcrypto can still run sessions over
|
|
43
|
+
* LambderPlainSessionCrypto and a memory store, which the shared
|
|
44
|
+
* resolver's message cannot know about.
|
|
45
45
|
*/
|
|
46
46
|
ready() {
|
|
47
47
|
this.cryptoPromise ??= (async () => {
|
|
@@ -56,19 +56,25 @@ export class LambderWebCrypto {
|
|
|
56
56
|
return this.cryptoPromise;
|
|
57
57
|
}
|
|
58
58
|
async sha256Hex(value) {
|
|
59
|
-
// ready() first, for
|
|
60
|
-
//
|
|
59
|
+
// ready() first, for its error message and the warmed Node crypto;
|
|
60
|
+
// the digest itself is the one every layer shares.
|
|
61
61
|
await this.ready();
|
|
62
62
|
return await sha256HexOf(value);
|
|
63
63
|
}
|
|
64
|
+
async hmacSha256Hex(key, value) {
|
|
65
|
+
const webCrypto = await this.ready();
|
|
66
|
+
const encoder = new TextEncoder();
|
|
67
|
+
const hmacKey = await webCrypto.subtle.importKey("raw", encoder.encode(key), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
68
|
+
return bytesToHexString(new Uint8Array(await webCrypto.subtle.sign("HMAC", hmacKey, encoder.encode(value))));
|
|
69
|
+
}
|
|
64
70
|
async randomHex(bytes) {
|
|
65
71
|
const webCrypto = await this.ready();
|
|
66
72
|
return bytesToHexString(webCrypto.getRandomValues(new Uint8Array(bytes)));
|
|
67
73
|
}
|
|
68
74
|
constantTimeEqual(a, b) {
|
|
69
|
-
//
|
|
70
|
-
// for this beats a JS loop the engine is free to optimize.
|
|
71
|
-
//
|
|
75
|
+
// Node's timingSafeEqual where the runtime has it: a primitive built
|
|
76
|
+
// for this beats a JS loop the engine is free to optimize. Browsers
|
|
77
|
+
// fall back to the loop.
|
|
72
78
|
const nodeCrypto = this.nodeCrypto;
|
|
73
79
|
if (nodeCrypto && a.length === b.length) {
|
|
74
80
|
const left = Buffer.from(a, "utf8");
|
|
@@ -89,6 +95,14 @@ export class LambderPlainSessionCrypto {
|
|
|
89
95
|
async sha256Hex(value) {
|
|
90
96
|
return bytesToHexString(new TextEncoder().encode(value));
|
|
91
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* The key and the value hex-encoded as a JSON pair rather than run
|
|
100
|
+
* together, so the pair stays unambiguous the way a keyed hash keeps it:
|
|
101
|
+
* no key and value can pass for another split of the same text.
|
|
102
|
+
*/
|
|
103
|
+
async hmacSha256Hex(key, value) {
|
|
104
|
+
return bytesToHexString(new TextEncoder().encode(JSON.stringify([key, value])));
|
|
105
|
+
}
|
|
92
106
|
async randomHex(bytes) {
|
|
93
107
|
const random = new Uint8Array(bytes);
|
|
94
108
|
for (let i = 0; i < bytes; i += 1)
|
|
@@ -20,13 +20,20 @@ export type LambderCreatedSession<SessionData = any> = {
|
|
|
20
20
|
* onto the same session record: same tokens, same cookies, the session
|
|
21
21
|
* itself is untouched. The refresh write and the sliding-expiration write
|
|
22
22
|
* share a single store write when both are due.
|
|
23
|
+
*
|
|
24
|
+
* A refresh result is written only over the data it was computed from: if
|
|
25
|
+
* the record's dataVersion moved in between (another request refreshed or
|
|
26
|
+
* wrote the data, or expireSessionDataAllByKey marked it stale), the result
|
|
27
|
+
* still serves this request but the newer write stands, and a marked record
|
|
28
|
+
* renews again on its next read.
|
|
23
29
|
*/
|
|
24
30
|
export type LambderSessionDataRefreshConfig<SessionData = any> = {
|
|
25
31
|
/** Seconds session.data stays valid before refresh() runs on read. */
|
|
26
32
|
ttlSeconds: number;
|
|
27
33
|
/**
|
|
28
34
|
* Rebuild session.data from its source of truth. Must be a pure
|
|
29
|
-
* derivation (concurrent reads may run it in parallel;
|
|
35
|
+
* derivation (concurrent reads may run it in parallel; the first result
|
|
36
|
+
* written stands, since each is written only over the data it read).
|
|
30
37
|
* Return null to end the session: the record is deleted and the read
|
|
31
38
|
* reports no session. Thrown errors fail the read as a
|
|
32
39
|
* LambderSessionDataRefreshError and leave the session untouched; catch
|
|
@@ -56,7 +63,7 @@ export declare class LambderSessionReadError extends Error {
|
|
|
56
63
|
export type LambderSessionManagerOptions<SessionData = any> = {
|
|
57
64
|
/** Where sessions rest: LambderDdbSessionStore, LambderMemorySessionStore, or your own. */
|
|
58
65
|
store: LambderSessionStore<SessionData>;
|
|
59
|
-
/**
|
|
66
|
+
/** The HMAC key that turns a sessionKey into the store's partition key, so a table read does not reveal which subject a session belongs to. */
|
|
60
67
|
sessionSalt: string;
|
|
61
68
|
enableSlidingExpiration?: boolean;
|
|
62
69
|
/** Min seconds between sliding-expiration writes. Default: max(60, 5% of TTL). */
|
|
@@ -69,13 +76,12 @@ export type LambderSessionManagerOptions<SessionData = any> = {
|
|
|
69
76
|
* Whether a string has the shape a minted session token has:
|
|
70
77
|
* `sessionKeyHash:secret`, both hex, neither longer than 1024 characters.
|
|
71
78
|
*
|
|
72
|
-
* It lives beside the code that mints and splits
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* writer of these cookies is the code that mints them.
|
|
79
|
+
* It lives beside the code that mints and splits the format, not the cookie
|
|
80
|
+
* reader, so the ceiling belongs to the model and anyone handing
|
|
81
|
+
* lookupSession a token they did not mint can ask it too. The session
|
|
82
|
+
* controller checks every candidate cookie with it before any store read, so
|
|
83
|
+
* a malformed one is "no session", never a read error. No legitimate cookie
|
|
84
|
+
* fails it, since only the minting code writes them.
|
|
79
85
|
*/
|
|
80
86
|
export declare const isMintedSessionToken: (token: string) => boolean;
|
|
81
87
|
/**
|
|
@@ -111,21 +117,16 @@ export default class LambderSessionManager<SessionData = any> {
|
|
|
111
117
|
*/
|
|
112
118
|
[LAMBDER_BACKEND_SWAP](store: LambderSessionStore<SessionData>): void;
|
|
113
119
|
/**
|
|
114
|
-
* The salted partition hash of a sessionKey:
|
|
115
|
-
*
|
|
120
|
+
* The salted partition hash of a sessionKey: HMAC-SHA256 with the salt
|
|
121
|
+
* as the key and the sessionKey as the message.
|
|
116
122
|
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* collide when the difference between their salts can be absorbed into a
|
|
125
|
-
* sessionKey ("ab" + "cd" and "a" + "bcd" hash alike). Two deployments
|
|
126
|
-
* over one table must therefore not stand in a prefix relationship over
|
|
127
|
-
* their salts. Separate tables, or salts that are independent random
|
|
128
|
-
* strings, both rule it out.
|
|
123
|
+
* Keyed rather than hashed over the two run together, because a plain
|
|
124
|
+
* concatenation cannot tell where the sessionKey ends and the salt
|
|
125
|
+
* begins: "ab" + "cd" and "a" + "bcd" would hash alike, so two
|
|
126
|
+
* deployments sharing one table could collide whenever the difference
|
|
127
|
+
* between their salts fits into a sessionKey. With the salt as the HMAC
|
|
128
|
+
* key the two inputs never meet in one string, so no choice of salts
|
|
129
|
+
* lets one deployment's subject land in another's partition.
|
|
129
130
|
*/
|
|
130
131
|
private sessionKeyHashOf;
|
|
131
132
|
/**
|
|
@@ -136,68 +137,145 @@ export default class LambderSessionManager<SessionData = any> {
|
|
|
136
137
|
*/
|
|
137
138
|
private hashToken;
|
|
138
139
|
createSession(sessionKey: string, data?: SessionData, ttlInSeconds?: number, options?: {
|
|
139
|
-
/**
|
|
140
|
+
/** The data refresh deadline to start with (regenerateSession starts its data due). */
|
|
140
141
|
dataExpiresAt?: number;
|
|
141
142
|
}): Promise<LambderCreatedSession<SessionData>>;
|
|
142
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Writes new session data onto the record, and nothing else: the expiry
|
|
145
|
+
* slides only in renewSession, which also re-issues the cookies, so a
|
|
146
|
+
* data write never moves the record's expiry away from the browser's.
|
|
147
|
+
* Returns the updated record, or null when the session was ended in
|
|
148
|
+
* between (the write does not bring it back).
|
|
149
|
+
*
|
|
150
|
+
* With dataRefresh, the write leaves dataExpiresAt where it is: only the
|
|
151
|
+
* dataRefresh callback's output is stamped fresh. The data an app writes
|
|
152
|
+
* is almost always the session's own with a field changed, still carrying
|
|
153
|
+
* whatever the callback derived last, so a write that pushed the deadline
|
|
154
|
+
* would let an app writing more often than ttlSeconds never refresh at
|
|
155
|
+
* all, and would cancel the due start regenerateSession gives a rotated
|
|
156
|
+
* session. The write is still conditioned on the dataVersion this record
|
|
157
|
+
* was read with. If that moved in between (expireSessionDataAllByKey
|
|
158
|
+
* marked the data stale, or another request refreshed it, possibly
|
|
159
|
+
* applying a revocation already), the data is written and marked due, so
|
|
160
|
+
* a revocation that landed during this request is not undone by data
|
|
161
|
+
* derived before it.
|
|
162
|
+
*
|
|
163
|
+
* The returned record carries the dataVersion this write produced only
|
|
164
|
+
* when a conditioned write applied, which is then exactly one past the
|
|
165
|
+
* version it named. Otherwise it keeps the version it was read with,
|
|
166
|
+
* which the store has already passed, so a later conditioned write from
|
|
167
|
+
* the same request answers "stale" and lands marked due: the safe side.
|
|
168
|
+
*/
|
|
169
|
+
updateSessionData(session: LambderSessionRecord<SessionData>, newData: SessionData): Promise<LambderSessionRecord<SessionData> | null>;
|
|
143
170
|
/**
|
|
144
171
|
* The record a token names, read and structurally checked, with nothing
|
|
145
|
-
* renewed.
|
|
146
|
-
* cookies can decide which
|
|
172
|
+
* renewed. Kept apart from renewSession so a caller weighing several
|
|
173
|
+
* candidate cookies can decide which is this visitor's BEFORE anything is
|
|
147
174
|
* written on their behalf: renewing slides an expiry and may run the
|
|
148
175
|
* app's dataRefresh callback, and a cookie a sibling host planted must
|
|
149
|
-
*
|
|
176
|
+
* get neither from the victim's traffic.
|
|
150
177
|
*/
|
|
151
178
|
lookupSession(sessionToken: string): Promise<LambderSessionRecord<SessionData> | null>;
|
|
152
179
|
/**
|
|
153
180
|
* The renewal half of a session read: the dataRefresh callback once its
|
|
154
181
|
* shelf life has passed, and the sliding-expiration write. Returns null
|
|
155
|
-
* when
|
|
156
|
-
*
|
|
182
|
+
* when the session is over: a refresh said so (a deleted or disabled
|
|
183
|
+
* login), or the record was deleted while this request read it (a
|
|
184
|
+
* logout, a password change), which a renewal must not undo.
|
|
157
185
|
*/
|
|
158
186
|
renewSession(session: LambderSessionRecord<SessionData>): Promise<LambderSessionRecord<SessionData> | null>;
|
|
159
187
|
/**
|
|
160
|
-
* Runs the dataRefresh callback
|
|
161
|
-
* persists the result onto the same record
|
|
162
|
-
*
|
|
163
|
-
*
|
|
188
|
+
* Runs the dataRefresh callback immediately, regardless of
|
|
189
|
+
* dataExpiresAt, and persists the result onto the same record, over the
|
|
190
|
+
* data it was computed from only (see LambderSessionDataRefreshConfig).
|
|
191
|
+
* Returns the refreshed session, or null when the callback ended it (the
|
|
192
|
+
* record is deleted) or the session is gone. The expiry does not slide
|
|
193
|
+
* here; renewSession slides it. Requires dataRefresh to be configured.
|
|
164
194
|
*/
|
|
165
195
|
refreshSessionData(session: LambderSessionRecord<SessionData>): Promise<LambderSessionRecord<SessionData> | null>;
|
|
166
196
|
/**
|
|
167
197
|
* Checks a record against the session token presented with it: the
|
|
168
198
|
* partition hash and the bearer secret the cookie carries, plus the
|
|
169
|
-
* structural checks and the expiry. This is the half a route needs
|
|
170
|
-
*
|
|
171
|
-
*
|
|
199
|
+
* structural checks and the expiry. This is the half a route needs; a
|
|
200
|
+
* record lookupSession just found by that token's own hash has already
|
|
201
|
+
* passed it, so the read path does not ask again.
|
|
172
202
|
*/
|
|
173
203
|
isSessionTokenValid(session: LambderSessionRecord<SessionData> | null, sessionToken: string | null): Promise<boolean>;
|
|
174
204
|
/**
|
|
175
205
|
* Checks a record against the CSRF token the request posted: the other
|
|
176
206
|
* half, asked of an API call and not of a route. Separate methods rather
|
|
177
|
-
* than one with a skip flag, because a boolean at the call site
|
|
178
|
-
*
|
|
179
|
-
* different places for different reasons.
|
|
207
|
+
* than one with a skip flag, because a boolean at the call site does not
|
|
208
|
+
* say which half it turns off.
|
|
180
209
|
*/
|
|
181
210
|
isSessionCsrfTokenValid(session: LambderSessionRecord<SessionData> | null, csrfToken: string | null): Promise<boolean>;
|
|
211
|
+
/** Deletes the session; false when there was none left to delete. */
|
|
182
212
|
deleteSession(session: LambderSessionRecord<SessionData>): Promise<boolean>;
|
|
183
|
-
/**
|
|
213
|
+
/**
|
|
214
|
+
* Deletes every session that shares the record's subject: "log this
|
|
215
|
+
* subject out everywhere". False when a rotation racing it may have left
|
|
216
|
+
* a session behind (see deleteAllUnder).
|
|
217
|
+
*/
|
|
184
218
|
deleteSessionAll(session: LambderSessionRecord<SessionData>): Promise<boolean>;
|
|
185
219
|
/**
|
|
186
220
|
* Deletes every session created for the given sessionKey (e.g. a user
|
|
187
221
|
* id): "log this subject out everywhere", without needing a fetched
|
|
188
|
-
* session record.
|
|
222
|
+
* session record. False when a rotation racing it may have left a
|
|
223
|
+
* session behind (see deleteAllUnder).
|
|
189
224
|
*/
|
|
190
225
|
deleteSessionAllByKey(sessionKey: string): Promise<boolean>;
|
|
226
|
+
/**
|
|
227
|
+
* Deletes every session of a subject, and answers whether it is sure
|
|
228
|
+
* none is left. A pass that finds a record it listed already gone lists
|
|
229
|
+
* again: a rotation writes its new record before deleting the old one
|
|
230
|
+
* (see regenerateSession), so the old one vanishing between this listing
|
|
231
|
+
* and this delete means a new record may have appeared after the
|
|
232
|
+
* listing. When the last pass the bound allows still finds one gone,
|
|
233
|
+
* such a record may be standing, so the call is logged and answers
|
|
234
|
+
* false: a caller ending a subject's sessions after a password change
|
|
235
|
+
* can run it again.
|
|
236
|
+
*/
|
|
191
237
|
private deleteAllUnder;
|
|
238
|
+
/**
|
|
239
|
+
* One write per session of a subject, a bounded number at a time: a
|
|
240
|
+
* subject with hundreds of sessions is not hundreds of round trips in a
|
|
241
|
+
* row, and not hundreds at once either.
|
|
242
|
+
*/
|
|
243
|
+
private forEachSessionOf;
|
|
192
244
|
/**
|
|
193
245
|
* Marks the data of every session of the given sessionKey stale, so each
|
|
194
246
|
* renews via dataRefresh on its next read: "this subject's roles or
|
|
195
|
-
* permissions changed, apply
|
|
196
|
-
* (deleteSessionAllByKey) and without waiting for the data TTL.
|
|
247
|
+
* permissions changed, apply that right away", without logging them out
|
|
248
|
+
* (deleteSessionAllByKey) and without waiting for the data TTL. Writes
|
|
197
249
|
* dataExpiresAt only, on records that still exist, so it neither
|
|
198
250
|
* resurrects a session deleted in between nor overwrites a concurrent
|
|
199
|
-
* write.
|
|
251
|
+
* write. The write moves each record's dataVersion, even when the
|
|
252
|
+
* deadline already reads this second, so a refresh or data write in
|
|
253
|
+
* flight, conditioned on the version it read, answers "stale" rather
|
|
254
|
+
* than landing data derived before the change. Requires dataRefresh to
|
|
255
|
+
* be configured.
|
|
200
256
|
*/
|
|
201
257
|
expireSessionDataAllByKey(sessionKey: string): Promise<boolean>;
|
|
202
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Replaces the session with a new one under new tokens, carrying over
|
|
260
|
+
* the data as the delete removed it rather than as this request read
|
|
261
|
+
* it, so data another request wrote meanwhile stays. Returns null, and
|
|
262
|
+
* leaves no new session behind, when the record was already gone or
|
|
263
|
+
* over: a logout, "log out everywhere" or a password change that landed
|
|
264
|
+
* during this request stays in force.
|
|
265
|
+
*
|
|
266
|
+
* The new record is written before the old one is deleted. The other
|
|
267
|
+
* way round, a subject-wide delete that listed the subject's sessions
|
|
268
|
+
* between the two would find neither, and the new session would outlive
|
|
269
|
+
* the password change it was racing. Written first, the new record is in
|
|
270
|
+
* that listing, or the old one still is: then either the subject-wide
|
|
271
|
+
* delete removes the old one before this delete does, and this delete
|
|
272
|
+
* takes the new one back out, or this delete removes it first, and the
|
|
273
|
+
* subject-wide delete, finding it gone, lists again (deleteAllUnder).
|
|
274
|
+
*
|
|
275
|
+
* With dataRefresh, the new session's data is due at once: a revocation
|
|
276
|
+
* marked by expireSessionDataAllByKey while the new record was being
|
|
277
|
+
* written may have passed it by, and due, its next read renews the data
|
|
278
|
+
* from the source of truth either way.
|
|
279
|
+
*/
|
|
280
|
+
regenerateSession(session: LambderSessionRecord<SessionData>): Promise<LambderCreatedSession<SessionData> | null>;
|
|
203
281
|
}
|