@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 +12 -0
- package/LICENSE +6 -0
- package/README.md +56 -2
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/package.json +37 -4
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
package/README.md
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @sansavision/yousaid-server
|
|
2
2
|
|
|
3
|
-
|
|
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
|
package/dist/index.d.ts
ADDED
|
@@ -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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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
|
+
}
|