@sansavision/yousaid-server 0.0.0-stage → 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/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@sansavision/yousaid-server` are recorded here. The
4
+ project follows semantic versioning for the stable surface listed in README.md.
5
+
6
+ ## 0.2.0
7
+
8
+ First npm release.
9
+
10
+ - Ships compiled ES2022 JavaScript with TypeScript declarations and declaration maps.
11
+ - Stable: `createYouSaidIdentityToken`, `IdentityClaims` and `IdentityTokenOptions`.
12
+ - `keyId` is required; tokens last 600 seconds by default and at most 900.
package/LICENSE ADDED
@@ -0,0 +1,6 @@
1
+ Copyright (c) 2026 Sansa Group AB
2
+
3
+ All rights reserved, Sansa Group AB. Licence terms pending owner and legal review.
4
+
5
+ No licence is granted to use, copy, modify or distribute this software until
6
+ Sansa Group AB publishes licence terms for it.
package/README.md CHANGED
@@ -1,3 +1,57 @@
1
- # Temporary Holding Version
1
+ # @sansavision/yousaid-server
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Server-side helper for YouSaid: sign a short-lived end-user identity token so
4
+ your browser code can send signals and read the inbox without ever holding your
5
+ product signing secret. Runs on Node 20 or later, Cloudflare Workers, Deno, Bun
6
+ and any runtime with Web Crypto.
7
+
8
+ ```sh
9
+ npm install @sansavision/yousaid-server
10
+ ```
11
+
12
+ ## Mint a token
13
+
14
+ ```ts
15
+ import { createYouSaidIdentityToken } from "@sansavision/yousaid-server";
16
+
17
+ const token = await createYouSaidIdentityToken(
18
+ {
19
+ sub: user.id, // your stable user id
20
+ org_id: "org_...", // your YouSaid workspace
21
+ product_id: "prd_...", // the product the signal belongs to
22
+ email: user.email, // optional, verified email; links earlier anonymous history
23
+ name: user.name, // optional
24
+ locale: "en", // optional
25
+ account_id: "acct_...", // optional, the customer account
26
+ },
27
+ process.env.YOUSAID_SIGNING_SECRET!,
28
+ { keyId: process.env.YOUSAID_KEY_ID! },
29
+ );
30
+ ```
31
+
32
+ Serve it from an authenticated route, for example `GET /api/yousaid-token`, with
33
+ `cache-control: no-store`, and pass a function that fetches it to
34
+ `YouSaidClient` or `YouSaidProvider`.
35
+
36
+ | Option | Meaning |
37
+ | --- | --- |
38
+ | `keyId` | Required. The key id issued with your product signing secret. Tokens are verified by this id. |
39
+ | `audience` | Defaults to `yousaid`. |
40
+ | `expiresInSeconds` | Defaults to 600, capped at 900. |
41
+
42
+ - Tokens are HS256 JWTs with a random nonce. Each state-changing request consumes the nonce, so mint a fresh token per write.
43
+ - Never ship the signing secret to the browser.
44
+ - Rotating the signing credential keeps the previous key valid for an overlap window so your servers can roll over.
45
+
46
+ ## Stability
47
+
48
+ Version 0.2.0 marks the whole package as **stable** under semantic versioning:
49
+ `createYouSaidIdentityToken` and its types `IdentityClaims` and
50
+ `IdentityTokenOptions`.
51
+
52
+ ## Licence
53
+
54
+ `UNLICENSED`. All rights reserved, Sansa Group AB. Licence terms are pending
55
+ owner and legal review; see LICENSE.
56
+
57
+ Documentation: https://yousaid.dev/docs/sdk
@@ -0,0 +1,19 @@
1
+ export type IdentityClaims = {
2
+ sub: string;
3
+ org_id: string;
4
+ product_id: string;
5
+ locale?: string;
6
+ account_id?: string;
7
+ /** Verified email. When present, YouSaid links this person's earlier anonymous history for the same email. */
8
+ email?: string;
9
+ name?: string;
10
+ };
11
+ export type IdentityTokenOptions = {
12
+ /** Key id of the product signing credential (required: tokens are verified by this id). */
13
+ keyId: string;
14
+ audience?: string;
15
+ /** Capped at 900 seconds. */
16
+ expiresInSeconds?: number;
17
+ };
18
+ export declare function createYouSaidIdentityToken(claims: IdentityClaims, secret: string, options: IdentityTokenOptions): Promise<string>;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8GAA8G;IAC9G,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,2FAA2F;IAC3F,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,MAAM,CAAC,CAoBjB"}
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
1
+ export async function createYouSaidIdentityToken(claims, secret, options) {
2
+ // YouSaid looks the signing key up by `kid`; there is no server-side "default" key, so a missing id could never verify.
3
+ if (!options?.keyId || typeof options.keyId !== "string" || options.keyId === "default") {
4
+ throw new TypeError("createYouSaidIdentityToken requires options.keyId: the YOUSAID_KEY_ID value issued with your product signing secret.");
5
+ }
6
+ const issuedAt = Math.floor(Date.now() / 1000);
7
+ const expiresIn = Math.min(options.expiresInSeconds ?? 600, 900);
8
+ const header = encode({ alg: "HS256", typ: "JWT", kid: options.keyId });
9
+ const payload = encode({
10
+ ...claims,
11
+ iss: "yousaid-customer-server",
12
+ aud: options.audience ?? "yousaid",
13
+ iat: issuedAt,
14
+ exp: issuedAt + expiresIn,
15
+ nonce: crypto.randomUUID(),
16
+ });
17
+ const input = `${header}.${payload}`;
18
+ const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
19
+ const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(input));
20
+ return `${input}.${base64Url(new Uint8Array(signature))}`;
21
+ }
22
+ function encode(value) {
23
+ return base64Url(new TextEncoder().encode(JSON.stringify(value)));
24
+ }
25
+ function base64Url(bytes) {
26
+ let binary = "";
27
+ for (const byte of bytes)
28
+ binary += String.fromCharCode(byte);
29
+ return btoa(binary).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
30
+ }
package/package.json CHANGED
@@ -1,6 +1,39 @@
1
1
  {
2
2
  "name": "@sansavision/yousaid-server",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Server-side YouSaid helpers: sign short-lived end-user identity tokens.",
5
+ "private": false,
6
+ "license": "UNLICENSED",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE",
21
+ "CHANGELOG.md"
22
+ ],
23
+ "sideEffects": false,
24
+ "scripts": {
25
+ "build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/Sansa-Organisation/yousaid.git",
30
+ "directory": "packages/server"
31
+ },
32
+ "homepage": "https://yousaid.dev/docs/sdk",
33
+ "engines": {
34
+ "node": ">=20.0.0"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }