@kispi/chat 0.1.2 → 0.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 +103 -8
- package/dist/{chunk-T3QOUFOM.js → chunk-5QQRM7BH.js} +2 -0
- package/dist/index.cjs +145 -14
- package/dist/index.d.cts +96 -6
- package/dist/index.d.ts +96 -6
- package/dist/index.js +144 -15
- package/dist/server/index.cjs +59 -10
- package/dist/server/index.d.cts +52 -1
- package/dist/server/index.d.ts +52 -1
- package/dist/server/index.js +57 -11
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
errorFrom
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-5QQRM7BH.js";
|
|
4
4
|
|
|
5
5
|
// src/server/api.ts
|
|
6
6
|
var ServerRest = class {
|
|
@@ -36,12 +36,15 @@ var ServerRest = class {
|
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
// src/server/guest.ts
|
|
40
|
+
import { createHmac as createHmac2, randomBytes } from "crypto";
|
|
41
|
+
|
|
39
42
|
// src/server/jwt.ts
|
|
40
43
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
41
44
|
function signHS256(header, claims, secret) {
|
|
42
45
|
const signingInput = `${b64url(JSON.stringify(header))}.${b64url(JSON.stringify(claims))}`;
|
|
43
|
-
const
|
|
44
|
-
return `${signingInput}.${
|
|
46
|
+
const mac2 = createHmac("sha256", secret).update(signingInput).digest();
|
|
47
|
+
return `${signingInput}.${mac2.toString("base64url")}`;
|
|
45
48
|
}
|
|
46
49
|
function macMatches(expected, given) {
|
|
47
50
|
return expected.length === given.length && timingSafeEqual(expected, given);
|
|
@@ -50,6 +53,32 @@ function b64url(s) {
|
|
|
50
53
|
return Buffer.from(s, "utf8").toString("base64url");
|
|
51
54
|
}
|
|
52
55
|
|
|
56
|
+
// src/server/guest.ts
|
|
57
|
+
var GuestIdPrefix = "g_";
|
|
58
|
+
var CredentialVersion = "g1";
|
|
59
|
+
var MacLabel = "kispi-chat/guest/v1\n";
|
|
60
|
+
var MinGuestSecretLength = 32;
|
|
61
|
+
function newGuestId() {
|
|
62
|
+
return `${GuestIdPrefix}${randomBytes(16).toString("base64url")}`;
|
|
63
|
+
}
|
|
64
|
+
function signGuestCredential(secret, userId) {
|
|
65
|
+
return `${CredentialVersion}.${userId}.${mac(secret, userId).toString("base64url")}`;
|
|
66
|
+
}
|
|
67
|
+
function verifyGuestCredential(secrets, credential) {
|
|
68
|
+
const parts = credential.split(".");
|
|
69
|
+
if (parts.length !== 3) return void 0;
|
|
70
|
+
const [version, userId, given] = parts;
|
|
71
|
+
if (version !== CredentialVersion || !userId.startsWith(GuestIdPrefix)) return void 0;
|
|
72
|
+
const givenMac = Buffer.from(given, "base64url");
|
|
73
|
+
for (const [i, secret] of secrets.entries()) {
|
|
74
|
+
if (macMatches(mac(secret, userId), givenMac)) return { userId, secretIndex: i };
|
|
75
|
+
}
|
|
76
|
+
return void 0;
|
|
77
|
+
}
|
|
78
|
+
function mac(secret, userId) {
|
|
79
|
+
return createHmac2("sha256", secret).update(MacLabel + userId).digest();
|
|
80
|
+
}
|
|
81
|
+
|
|
53
82
|
// src/server/token.ts
|
|
54
83
|
var MaxTokenTtlSeconds = 24 * 60 * 60;
|
|
55
84
|
var DefaultTokenTtlSeconds = 60 * 60;
|
|
@@ -81,7 +110,7 @@ function tokenClaims(input, now) {
|
|
|
81
110
|
}
|
|
82
111
|
|
|
83
112
|
// src/server/webhooks.ts
|
|
84
|
-
import { createHmac as
|
|
113
|
+
import { createHmac as createHmac3 } from "crypto";
|
|
85
114
|
var SignatureToleranceMs = 5 * 60 * 1e3;
|
|
86
115
|
var EventBeforePublish = "before_publish";
|
|
87
116
|
function verifyWebhook(secret, headers, rawBody, options = {}) {
|
|
@@ -119,7 +148,7 @@ function verifyWebhook(secret, headers, rawBody, options = {}) {
|
|
|
119
148
|
if (age > tolerance) {
|
|
120
149
|
throw new Error(`webhook: signature is ${Math.round(age / 1e3)}s old, tolerance is ${tolerance / 1e3}s`);
|
|
121
150
|
}
|
|
122
|
-
const expected =
|
|
151
|
+
const expected = createHmac3("sha256", secret).update(`${timestamp}
|
|
123
152
|
${event}
|
|
124
153
|
${delivery}
|
|
125
154
|
${rawBody}`).digest();
|
|
@@ -153,13 +182,29 @@ function createChatServer(options) {
|
|
|
153
182
|
throw new TypeError("keyId is required: it becomes the token's kid, which tells the server which key signed it");
|
|
154
183
|
}
|
|
155
184
|
const rest = new ServerRest(options.url, options.secretKey, options.fetch ?? globalThis.fetch.bind(globalThis));
|
|
185
|
+
const guestSecrets = options.guestSecret === void 0 ? [] : typeof options.guestSecret === "string" ? [options.guestSecret] : options.guestSecret;
|
|
186
|
+
for (const secret of guestSecrets) {
|
|
187
|
+
if (secret.length < MinGuestSecretLength) {
|
|
188
|
+
throw new RangeError(`guestSecret must be at least ${MinGuestSecretLength} characters`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const sign = (input) => signHS256({ alg: "HS256", typ: "JWT", kid: options.keyId }, tokenClaims(input, /* @__PURE__ */ new Date()), options.secretKey);
|
|
156
192
|
return {
|
|
157
|
-
token
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
193
|
+
token: sign,
|
|
194
|
+
guest(input) {
|
|
195
|
+
const signer = guestSecrets[0];
|
|
196
|
+
if (signer === void 0) {
|
|
197
|
+
throw new TypeError("guestSecret was not given to createChatServer, so guest credentials cannot be signed");
|
|
198
|
+
}
|
|
199
|
+
const { credential, ...claims } = input;
|
|
200
|
+
const verified = credential ? verifyGuestCredential(guestSecrets, credential) : void 0;
|
|
201
|
+
const userId = verified?.userId ?? newGuestId();
|
|
202
|
+
return {
|
|
203
|
+
userId,
|
|
204
|
+
credential: verified !== void 0 && verified.secretIndex === 0 ? credential : signGuestCredential(signer, userId),
|
|
205
|
+
token: sign({ ...claims, userId }),
|
|
206
|
+
created: verified === void 0
|
|
207
|
+
};
|
|
163
208
|
},
|
|
164
209
|
rooms: {
|
|
165
210
|
ensure: (input) => rest.call("PUT", "/v1/rooms", input),
|
|
@@ -250,6 +295,7 @@ function createChatServer(options) {
|
|
|
250
295
|
}
|
|
251
296
|
export {
|
|
252
297
|
DefaultTokenTtlSeconds,
|
|
298
|
+
GuestIdPrefix,
|
|
253
299
|
MaxTokenTtlSeconds,
|
|
254
300
|
SignatureToleranceMs,
|
|
255
301
|
createChatServer
|