@nevermined-io/ui-widgets-server 0.5.6 → 0.5.8

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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # @nevermined-io/ui-widgets-server
2
2
 
3
- Server-side helper for the Nevermined UI Widgets. Mints short-lived **init tokens** that the browser SDK ([`@nevermined-io/ui-widgets`](https://www.npmjs.com/package/@nevermined-io/ui-widgets)) exchanges for an authenticated widget session.
3
+ Server-side helper for the Nevermined UI Widgets. Mints a **widget session** by calling the Nevermined API with your widget key secret and returns the session the browser SDK ([`@nevermined-io/ui-widgets`](https://www.npmjs.com/package/@nevermined-io/ui-widgets)) consumes directly.
4
4
 
5
- This package is intended to run **only on your backend**. It signs JWTs with your widget key secret, which must never be exposed to the browser.
5
+ This package is intended to run **only on your backend**. It transmits your widget key `rawSecret` server-to-server; the secret must never be exposed to the browser.
6
6
 
7
7
  ## Install
8
8
 
@@ -12,32 +12,33 @@ npm install @nevermined-io/ui-widgets-server
12
12
  pnpm add @nevermined-io/ui-widgets-server
13
13
  ```
14
14
 
15
- Requires Node 18+ (uses Web Crypto via [`jose`](https://www.npmjs.com/package/jose)).
15
+ Requires Node 18+ (uses the global `fetch`).
16
16
 
17
17
  ## Quick start
18
18
 
19
- In your organization's backend, create an endpoint that returns a fresh init token for the currently logged-in user:
19
+ In your organization's backend, create an endpoint that returns a fresh widget session for the currently logged-in user:
20
20
 
21
21
  ```ts
22
22
  import express from 'express'
23
- import { createInitToken } from '@nevermined-io/ui-widgets-server'
23
+ import { createWidgetSession } from '@nevermined-io/ui-widgets-server'
24
24
 
25
25
  const app = express()
26
26
 
27
- app.get('/api/widget-init-token', async (req, res) => {
27
+ app.get('/api/widget-session', async (req, res) => {
28
28
  // Authenticate the request your normal way and resolve the user's email.
29
29
  // The email is the canonical Nevermined identity — a user that later logs
30
30
  // into nevermined.dev directly with the same email is resolved to the
31
31
  // same Privy user, the same wallet, and the same widget profile.
32
32
  const email = req.user.email
33
33
 
34
- const initToken = await createInitToken({
34
+ const session = await createWidgetSession({
35
35
  email,
36
36
  orgId: process.env.NVM_ORG_ID!,
37
- secretKey: process.env.NVM_WIDGET_KEY_SECRET!, // never expose to the browser
37
+ rawSecret: process.env.NVM_WIDGET_KEY_SECRET!, // never expose to the browser
38
+ apiBaseUrl: 'https://api.sandbox.nevermined.app',
38
39
  })
39
40
 
40
- res.json({ initToken })
41
+ res.json(session)
41
42
  })
42
43
  ```
43
44
 
@@ -46,50 +47,51 @@ Then, in the browser:
46
47
  ```ts
47
48
  import { NeverminedWidgets } from '@nevermined-io/ui-widgets'
48
49
 
49
- const { initToken } = await fetch('/api/widget-init-token').then(r => r.json())
50
+ const session = await fetch('/api/widget-session').then((r) => r.json())
50
51
 
51
52
  const nvm = await NeverminedWidgets.initialize({
52
- initToken,
53
+ session,
53
54
  environment: 'sandbox',
54
55
  })
55
56
  ```
56
57
 
57
58
  ## API
58
59
 
59
- ### `createInitToken(options): Promise<string>`
60
+ ### `createWidgetSession(options): Promise<WidgetSession>`
60
61
 
61
- Returns a signed JWT (HS256) that the browser SDK passes to `POST /api/v1/widgets/session`. The token's audience is `nvm:widgets` and it carries the user's email (`email`, the canonical Nevermined identity) and organization id (`orgId`) as claims.
62
+ POSTs `{ orgId, email, rawSecret }` to `${apiBaseUrl}/api/v1/widgets/session` and returns the session response. Forward the result verbatim to the browser SDK.
62
63
 
63
64
  #### Options
64
65
 
65
- | Field | Type | Required | Default | Description |
66
- | ------------ | -------- | -------- | ------- | ----------- |
67
- | `email` | `string` | yes | — | The end user's email. Becomes the JWT `email` claim and the canonical Nevermined identity — a user that logs into nevermined.dev directly with the same email is resolved to the same Privy user, wallet, and profile. |
68
- | `orgId` | `string` | yes | — | Your Nevermined organization id. |
69
- | `secretKey` | `string` | yes | — | The widget key secret (`wk_...`) issued in the Nevermined dashboard. **Server-side only.** |
70
- | `expiresIn` | `string` | no | `'5m'` | JWT lifetime — any [`jose`-compatible](https://github.com/panva/jose) duration string. The default is intentionally short: the browser exchanges the token immediately for a long-lived session. |
66
+ | Field | Type | Required | Description |
67
+ | ------------ | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
68
+ | `email` | `string` | yes | The end user's email — the canonical Nevermined identity. A user that logs into nevermined.dev directly with the same email is resolved to the same Privy user, wallet, and profile. Trimmed and lowercased before sending. |
69
+ | `orgId` | `string` | yes | Your Nevermined organization id. |
70
+ | `rawSecret` | `string` | yes | The widget key secret (`wk_...`) issued in the Nevermined dashboard. **Server-side only.** F-055: the API stores only its SHA-256 hash. |
71
+ | `apiBaseUrl` | `string` | yes | Nevermined API base URL — e.g. `https://api.sandbox.nevermined.app`. The helper appends `/api/v1/widgets/session`. |
72
+ | `fetch` | `typeof fetch` | no | Override the global `fetch` (useful in tests). |
71
73
 
72
- #### Token shape
74
+ #### Response shape
73
75
 
74
76
  ```ts
75
- interface InitTokenPayload {
76
- sub: string // mirrors `email` (legacy claim, kept for compatibility)
77
- email: string // canonical Nevermined identity
78
- orgId: string
79
- aud: 'nvm:widgets'
80
- iat: number
81
- exp: number
77
+ interface WidgetSession {
78
+ sessionToken: string
79
+ userId: string
80
+ userWallet: string
81
+ apiKeyHash: string
82
+ expiresAt: string // ISO timestamp
82
83
  }
83
84
  ```
84
85
 
85
- #### Breaking change vs `< 0.5.0`
86
+ #### Breaking change vs `< 0.6.0`
86
87
 
87
- The `userId` parameter was renamed to `email` and is now required to be a real email address. The widget session API resolves the Privy user via `getByEmailAddress` and only falls back to creating a new user if no match exists; an arbitrary `userId` no longer maps to a deterministic Privy user. Update your backend integration to forward the user's email to `createInitToken` instead of an internal id.
88
+ - `createInitToken` has been removed. F-055 (#1468): the iframe-bound init JWT model required the API to store widget secrets in a reversible form. Hashed storage replaces that with a server-to-server credential exchange: the integrator backend calls the Nevermined API directly with `rawSecret` and forwards the session to the iframe.
89
+ - The new helper is `createWidgetSession`. The `secretKey` option is renamed `rawSecret`; an `apiBaseUrl` option is now required. The return value is the full `WidgetSession` object, not a JWT string.
90
+ - The browser SDK now accepts the session object directly via `NeverminedWidgets.initialize({ session, environment })`; the iframe no longer exchanges a token at startup.
88
91
 
89
92
  ## Security notes
90
93
 
91
- - Treat `secretKey` like a database password: never inline it in client code, commit it, or send it through an unencrypted channel. If it leaks, rotate the widget key in the dashboard immediately.
92
- - Issue a new init token per session — they are short-lived by design. Do not cache them across users.
94
+ - Treat `rawSecret` like a database password: never inline it in client code, commit it, or send it through an unencrypted channel. If it leaks, rotate the widget key in the dashboard immediately.
93
95
  - Pair this with `allowedOrigins` on the widget key so even a leaked secret can't mint sessions for arbitrary origins.
94
96
 
95
97
  ## License
@@ -0,0 +1,12 @@
1
+ import type { CreateWidgetSessionOptions, WidgetSession } from './types.js';
2
+ /**
3
+ * Server-side helper that mints a widget session on the integrator's backend
4
+ * and returns the response. Forward the result verbatim to the browser SDK
5
+ * as `NeverminedWidgets.initialize({ session, environment })`.
6
+ *
7
+ * F-055 (#1468): the widget key `rawSecret` is the only credential the API
8
+ * accepts. It is hashed (SHA-256) at rest and constant-time compared on each
9
+ * call. It must remain on the backend — never expose it to the browser.
10
+ */
11
+ export declare function createWidgetSession(options: CreateWidgetSessionOptions): Promise<WidgetSession>;
12
+ //# sourceMappingURL=create-widget-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-widget-session.d.ts","sourceRoot":"","sources":["../src/create-widget-session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,0BAA0B,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAgB3E;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,aAAa,CAAC,CA6CxB"}
@@ -0,0 +1,57 @@
1
+ import { normalizeEmail } from './normalize-email.js';
2
+ const SESSION_ENDPOINT = '/api/v1/widgets/session';
3
+ function isWidgetSession(value) {
4
+ if (typeof value !== 'object' || value === null) return false;
5
+ const v = value;
6
+ return typeof v.sessionToken === 'string' && typeof v.userId === 'string' && typeof v.userWallet === 'string' && typeof v.apiKeyHash === 'string' && typeof v.expiresAt === 'string';
7
+ }
8
+ /**
9
+ * Server-side helper that mints a widget session on the integrator's backend
10
+ * and returns the response. Forward the result verbatim to the browser SDK
11
+ * as `NeverminedWidgets.initialize({ session, environment })`.
12
+ *
13
+ * F-055 (#1468): the widget key `rawSecret` is the only credential the API
14
+ * accepts. It is hashed (SHA-256) at rest and constant-time compared on each
15
+ * call. It must remain on the backend — never expose it to the browser.
16
+ */ export async function createWidgetSession(options) {
17
+ const { rawSecret, orgId, apiBaseUrl } = options;
18
+ if (!rawSecret?.trim()) throw new Error('rawSecret is required');
19
+ if (!orgId?.trim()) throw new Error('orgId is required');
20
+ if (!apiBaseUrl?.trim()) throw new Error('apiBaseUrl is required');
21
+ const normalizedEmail = normalizeEmail(options.email);
22
+ const fetchImpl = options.fetch ?? fetch;
23
+ const url = `${apiBaseUrl.replace(/\/$/, '')}${SESSION_ENDPOINT}`;
24
+ let response;
25
+ try {
26
+ response = await fetchImpl(url, {
27
+ method: 'POST',
28
+ headers: {
29
+ 'Content-Type': 'application/json',
30
+ Accept: 'application/json'
31
+ },
32
+ body: JSON.stringify({
33
+ orgId,
34
+ email: normalizedEmail,
35
+ rawSecret
36
+ })
37
+ });
38
+ } catch (cause) {
39
+ throw new Error(`Widget session request to ${url} failed: ${cause instanceof Error ? cause.message : String(cause)}`);
40
+ }
41
+ if (!response.ok) {
42
+ const body = await response.text().catch(()=>'');
43
+ throw new Error(`Widget session request to ${url} failed with status ${response.status}: ${body}`);
44
+ }
45
+ let payload;
46
+ try {
47
+ payload = await response.json();
48
+ } catch (cause) {
49
+ throw new Error(`Widget session response from ${url} was not valid JSON: ${cause instanceof Error ? cause.message : String(cause)}`);
50
+ }
51
+ if (!isWidgetSession(payload)) {
52
+ throw new Error(`Widget session response from ${url} is missing required fields`);
53
+ }
54
+ return payload;
55
+ }
56
+
57
+ //# sourceMappingURL=create-widget-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/create-widget-session.ts"],"sourcesContent":["import { normalizeEmail } from './normalize-email.js'\nimport type { CreateWidgetSessionOptions, WidgetSession } from './types.js'\n\nconst SESSION_ENDPOINT = '/api/v1/widgets/session'\n\nfunction isWidgetSession(value: unknown): value is WidgetSession {\n if (typeof value !== 'object' || value === null) return false\n const v = value as Record<string, unknown>\n return (\n typeof v.sessionToken === 'string' &&\n typeof v.userId === 'string' &&\n typeof v.userWallet === 'string' &&\n typeof v.apiKeyHash === 'string' &&\n typeof v.expiresAt === 'string'\n )\n}\n\n/**\n * Server-side helper that mints a widget session on the integrator's backend\n * and returns the response. Forward the result verbatim to the browser SDK\n * as `NeverminedWidgets.initialize({ session, environment })`.\n *\n * F-055 (#1468): the widget key `rawSecret` is the only credential the API\n * accepts. It is hashed (SHA-256) at rest and constant-time compared on each\n * call. It must remain on the backend — never expose it to the browser.\n */\nexport async function createWidgetSession(\n options: CreateWidgetSessionOptions,\n): Promise<WidgetSession> {\n const { rawSecret, orgId, apiBaseUrl } = options\n\n if (!rawSecret?.trim()) throw new Error('rawSecret is required')\n if (!orgId?.trim()) throw new Error('orgId is required')\n if (!apiBaseUrl?.trim()) throw new Error('apiBaseUrl is required')\n\n const normalizedEmail = normalizeEmail(options.email)\n const fetchImpl = options.fetch ?? fetch\n const url = `${apiBaseUrl.replace(/\\/$/, '')}${SESSION_ENDPOINT}`\n\n let response: Response\n try {\n response = await fetchImpl(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ orgId, email: normalizedEmail, rawSecret }),\n })\n } catch (cause) {\n throw new Error(\n `Widget session request to ${url} failed: ${cause instanceof Error ? cause.message : String(cause)}`,\n )\n }\n\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n throw new Error(\n `Widget session request to ${url} failed with status ${response.status}: ${body}`,\n )\n }\n\n let payload: unknown\n try {\n payload = await response.json()\n } catch (cause) {\n throw new Error(\n `Widget session response from ${url} was not valid JSON: ${cause instanceof Error ? cause.message : String(cause)}`,\n )\n }\n\n if (!isWidgetSession(payload)) {\n throw new Error(`Widget session response from ${url} is missing required fields`)\n }\n\n return payload\n}\n"],"names":["normalizeEmail","SESSION_ENDPOINT","isWidgetSession","value","v","sessionToken","userId","userWallet","apiKeyHash","expiresAt","createWidgetSession","options","rawSecret","orgId","apiBaseUrl","trim","Error","normalizedEmail","email","fetchImpl","fetch","url","replace","response","method","headers","Accept","body","JSON","stringify","cause","message","String","ok","text","catch","status","payload","json"],"mappings":"AAAA,SAASA,cAAc,QAAQ,uBAAsB;AAGrD,MAAMC,mBAAmB;AAEzB,SAASC,gBAAgBC,KAAc;IACrC,IAAI,OAAOA,UAAU,YAAYA,UAAU,MAAM,OAAO;IACxD,MAAMC,IAAID;IACV,OACE,OAAOC,EAAEC,YAAY,KAAK,YAC1B,OAAOD,EAAEE,MAAM,KAAK,YACpB,OAAOF,EAAEG,UAAU,KAAK,YACxB,OAAOH,EAAEI,UAAU,KAAK,YACxB,OAAOJ,EAAEK,SAAS,KAAK;AAE3B;AAEA;;;;;;;;CAQC,GACD,OAAO,eAAeC,oBACpBC,OAAmC;IAEnC,MAAM,EAAEC,SAAS,EAAEC,KAAK,EAAEC,UAAU,EAAE,GAAGH;IAEzC,IAAI,CAACC,WAAWG,QAAQ,MAAM,IAAIC,MAAM;IACxC,IAAI,CAACH,OAAOE,QAAQ,MAAM,IAAIC,MAAM;IACpC,IAAI,CAACF,YAAYC,QAAQ,MAAM,IAAIC,MAAM;IAEzC,MAAMC,kBAAkBjB,eAAeW,QAAQO,KAAK;IACpD,MAAMC,YAAYR,QAAQS,KAAK,IAAIA;IACnC,MAAMC,MAAM,GAAGP,WAAWQ,OAAO,CAAC,OAAO,MAAMrB,kBAAkB;IAEjE,IAAIsB;IACJ,IAAI;QACFA,WAAW,MAAMJ,UAAUE,KAAK;YAC9BG,QAAQ;YACRC,SAAS;gBAAE,gBAAgB;gBAAoBC,QAAQ;YAAmB;YAC1EC,MAAMC,KAAKC,SAAS,CAAC;gBAAEhB;gBAAOK,OAAOD;gBAAiBL;YAAU;QAClE;IACF,EAAE,OAAOkB,OAAO;QACd,MAAM,IAAId,MACR,CAAC,0BAA0B,EAAEK,IAAI,SAAS,EAAES,iBAAiBd,QAAQc,MAAMC,OAAO,GAAGC,OAAOF,QAAQ;IAExG;IAEA,IAAI,CAACP,SAASU,EAAE,EAAE;QAChB,MAAMN,OAAO,MAAMJ,SAASW,IAAI,GAAGC,KAAK,CAAC,IAAM;QAC/C,MAAM,IAAInB,MACR,CAAC,0BAA0B,EAAEK,IAAI,oBAAoB,EAAEE,SAASa,MAAM,CAAC,EAAE,EAAET,MAAM;IAErF;IAEA,IAAIU;IACJ,IAAI;QACFA,UAAU,MAAMd,SAASe,IAAI;IAC/B,EAAE,OAAOR,OAAO;QACd,MAAM,IAAId,MACR,CAAC,6BAA6B,EAAEK,IAAI,qBAAqB,EAAES,iBAAiBd,QAAQc,MAAMC,OAAO,GAAGC,OAAOF,QAAQ;IAEvH;IAEA,IAAI,CAAC5B,gBAAgBmC,UAAU;QAC7B,MAAM,IAAIrB,MAAM,CAAC,6BAA6B,EAAEK,IAAI,2BAA2B,CAAC;IAClF;IAEA,OAAOgB;AACT"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { createInitToken } from './create-init-token.js';
2
- export type { CreateInitTokenOptions, InitTokenPayload } from './types.js';
1
+ export { createWidgetSession } from './create-widget-session.js';
2
+ export type { CreateWidgetSessionOptions, WidgetSession } from './types.js';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,YAAY,EAAE,0BAA0B,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { createInitToken } from './create-init-token.js';
1
+ export { createWidgetSession } from './create-widget-session.js';
2
2
 
3
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { createInitToken } from './create-init-token.js'\nexport type { CreateInitTokenOptions, InitTokenPayload } from './types.js'\n"],"names":["createInitToken"],"mappings":"AAAA,SAASA,eAAe,QAAQ,yBAAwB"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { createWidgetSession } from './create-widget-session.js'\nexport type { CreateWidgetSessionOptions, WidgetSession } from './types.js'\n"],"names":["createWidgetSession"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,6BAA4B"}
package/dist/types.d.ts CHANGED
@@ -1,32 +1,41 @@
1
- export interface CreateInitTokenOptions {
1
+ export interface CreateWidgetSessionOptions {
2
2
  /**
3
3
  * The end user's email — the canonical Nevermined identity (#1520). The
4
4
  * widget session API uses this to resolve or provision the Privy user.
5
5
  *
6
6
  * Validated and normalized at the SDK boundary: trimmed, lowercased, capped
7
7
  * at 254 chars (RFC 5321), and matched against a basic format regex. The
8
- * normalized value is what ends up in the JWT — callers should not rely on
8
+ * normalized value is what the API receives — callers should not rely on
9
9
  * case being preserved.
10
10
  */
11
11
  email: string;
12
+ /** Nevermined organization id (from the dashboard). */
12
13
  orgId: string;
13
- secretKey: string;
14
- expiresIn?: string;
15
- }
16
- export interface InitTokenPayload {
17
14
  /**
18
- * Mirrors `email` so the widget session API's non-empty existence check on
19
- * `sub` passes without a parallel API change. Do NOT read `sub` for identity
20
- * downstream — read `email` instead. This claim will be dropped once the
21
- * API stops reading `sub` (follow-up to #1520).
22
- *
23
- * @deprecated Read `email` instead. Kept temporarily for API compatibility.
15
+ * F-055: raw widget key secret (`wk_...`) issued in the dashboard. The
16
+ * Nevermined API stores only its SHA-256 hash; the plaintext value must
17
+ * stay on the integrator's backend and **never** travel through a browser.
24
18
  */
25
- sub: string;
26
- email: string;
27
- orgId: string;
28
- aud: string;
29
- iat: number;
30
- exp: number;
19
+ rawSecret: string;
20
+ /**
21
+ * Nevermined API base URL — e.g. `https://api.sandbox.nevermined.app`. The
22
+ * SDK appends `/api/v1/widgets/session` itself. Required so the helper
23
+ * works from arbitrary backends without an env coupling.
24
+ */
25
+ apiBaseUrl: string;
26
+ /**
27
+ * Optional fetch override — useful in tests or in environments that ship
28
+ * their own HTTP client. Defaults to the global `fetch`.
29
+ */
30
+ fetch?: typeof fetch;
31
+ }
32
+ export interface WidgetSession {
33
+ /** Widget session JWT — forward to the browser SDK as `session.sessionToken`. */
34
+ sessionToken: string;
35
+ userId: string;
36
+ userWallet: string;
37
+ apiKeyHash: string;
38
+ /** ISO timestamp at which the session token expires. */
39
+ expiresAt: string;
31
40
  }
32
41
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;OAQG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;OAOG;IACH,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,0BAA0B;IACzC;;;;;;;;OAQG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAA;CAClB"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["export interface CreateInitTokenOptions {\n /**\n * The end user's email — the canonical Nevermined identity (#1520). The\n * widget session API uses this to resolve or provision the Privy user.\n *\n * Validated and normalized at the SDK boundary: trimmed, lowercased, capped\n * at 254 chars (RFC 5321), and matched against a basic format regex. The\n * normalized value is what ends up in the JWT — callers should not rely on\n * case being preserved.\n */\n email: string\n orgId: string\n secretKey: string\n expiresIn?: string // default '5m'\n}\n\nexport interface InitTokenPayload {\n /**\n * Mirrors `email` so the widget session API's non-empty existence check on\n * `sub` passes without a parallel API change. Do NOT read `sub` for identity\n * downstream — read `email` instead. This claim will be dropped once the\n * API stops reading `sub` (follow-up to #1520).\n *\n * @deprecated Read `email` instead. Kept temporarily for API compatibility.\n */\n sub: string\n email: string\n orgId: string\n aud: string // 'nvm:widgets'\n iat: number\n exp: number\n}\n"],"names":[],"mappings":"AAgBA,WAeC"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["export interface CreateWidgetSessionOptions {\n /**\n * The end user's email — the canonical Nevermined identity (#1520). The\n * widget session API uses this to resolve or provision the Privy user.\n *\n * Validated and normalized at the SDK boundary: trimmed, lowercased, capped\n * at 254 chars (RFC 5321), and matched against a basic format regex. The\n * normalized value is what the API receives — callers should not rely on\n * case being preserved.\n */\n email: string\n /** Nevermined organization id (from the dashboard). */\n orgId: string\n /**\n * F-055: raw widget key secret (`wk_...`) issued in the dashboard. The\n * Nevermined API stores only its SHA-256 hash; the plaintext value must\n * stay on the integrator's backend and **never** travel through a browser.\n */\n rawSecret: string\n /**\n * Nevermined API base URL — e.g. `https://api.sandbox.nevermined.app`. The\n * SDK appends `/api/v1/widgets/session` itself. Required so the helper\n * works from arbitrary backends without an env coupling.\n */\n apiBaseUrl: string\n /**\n * Optional fetch override — useful in tests or in environments that ship\n * their own HTTP client. Defaults to the global `fetch`.\n */\n fetch?: typeof fetch\n}\n\nexport interface WidgetSession {\n /** Widget session JWT — forward to the browser SDK as `session.sessionToken`. */\n sessionToken: string\n userId: string\n userWallet: string\n apiKeyHash: string\n /** ISO timestamp at which the session token expires. */\n expiresAt: string\n}\n"],"names":[],"mappings":"AAgCA,WAQC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nevermined-io/ui-widgets-server",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -17,9 +17,7 @@
17
17
  "dist",
18
18
  "!**/*.tsbuildinfo"
19
19
  ],
20
- "dependencies": {
21
- "jose": "4.15.9"
22
- },
20
+ "dependencies": {},
23
21
  "nx": {
24
22
  "targets": {
25
23
  "build": {
@@ -1,3 +0,0 @@
1
- import type { CreateInitTokenOptions } from './types.js';
2
- export declare function createInitToken(options: CreateInitTokenOptions): Promise<string>;
3
- //# sourceMappingURL=create-init-token.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create-init-token.d.ts","sourceRoot":"","sources":["../src/create-init-token.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAKxD,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBtF"}
@@ -1,22 +0,0 @@
1
- import { SignJWT } from 'jose';
2
- import { normalizeEmail } from './normalize-email.js';
3
- const WIDGET_AUDIENCE = 'nvm:widgets';
4
- const DEFAULT_EXPIRES_IN = '5m';
5
- export async function createInitToken(options) {
6
- const { email, orgId, secretKey, expiresIn = DEFAULT_EXPIRES_IN } = options;
7
- if (!secretKey?.trim()) throw new Error('secretKey is required');
8
- if (!orgId?.trim()) throw new Error('orgId is required');
9
- const normalizedEmail = normalizeEmail(email);
10
- const secret = new TextEncoder().encode(secretKey);
11
- // `sub` is set to the email so the API's existence check on the claim
12
- // continues to pass without a parallel API change. The downstream
13
- // identity resolution uses the explicit `email` claim, not `sub`.
14
- return new SignJWT({
15
- orgId,
16
- email: normalizedEmail
17
- }).setProtectedHeader({
18
- alg: 'HS256'
19
- }).setSubject(normalizedEmail).setAudience(WIDGET_AUDIENCE).setIssuedAt().setExpirationTime(expiresIn).sign(secret);
20
- }
21
-
22
- //# sourceMappingURL=create-init-token.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/create-init-token.ts"],"sourcesContent":["import { SignJWT } from 'jose'\nimport { normalizeEmail } from './normalize-email.js'\nimport type { CreateInitTokenOptions } from './types.js'\n\nconst WIDGET_AUDIENCE = 'nvm:widgets'\nconst DEFAULT_EXPIRES_IN = '5m'\n\nexport async function createInitToken(options: CreateInitTokenOptions): Promise<string> {\n const { email, orgId, secretKey, expiresIn = DEFAULT_EXPIRES_IN } = options\n\n if (!secretKey?.trim()) throw new Error('secretKey is required')\n if (!orgId?.trim()) throw new Error('orgId is required')\n\n const normalizedEmail = normalizeEmail(email)\n\n const secret = new TextEncoder().encode(secretKey)\n\n // `sub` is set to the email so the API's existence check on the claim\n // continues to pass without a parallel API change. The downstream\n // identity resolution uses the explicit `email` claim, not `sub`.\n return new SignJWT({ orgId, email: normalizedEmail })\n .setProtectedHeader({ alg: 'HS256' })\n .setSubject(normalizedEmail)\n .setAudience(WIDGET_AUDIENCE)\n .setIssuedAt()\n .setExpirationTime(expiresIn)\n .sign(secret)\n}\n"],"names":["SignJWT","normalizeEmail","WIDGET_AUDIENCE","DEFAULT_EXPIRES_IN","createInitToken","options","email","orgId","secretKey","expiresIn","trim","Error","normalizedEmail","secret","TextEncoder","encode","setProtectedHeader","alg","setSubject","setAudience","setIssuedAt","setExpirationTime","sign"],"mappings":"AAAA,SAASA,OAAO,QAAQ,OAAM;AAC9B,SAASC,cAAc,QAAQ,uBAAsB;AAGrD,MAAMC,kBAAkB;AACxB,MAAMC,qBAAqB;AAE3B,OAAO,eAAeC,gBAAgBC,OAA+B;IACnE,MAAM,EAAEC,KAAK,EAAEC,KAAK,EAAEC,SAAS,EAAEC,YAAYN,kBAAkB,EAAE,GAAGE;IAEpE,IAAI,CAACG,WAAWE,QAAQ,MAAM,IAAIC,MAAM;IACxC,IAAI,CAACJ,OAAOG,QAAQ,MAAM,IAAIC,MAAM;IAEpC,MAAMC,kBAAkBX,eAAeK;IAEvC,MAAMO,SAAS,IAAIC,cAAcC,MAAM,CAACP;IAExC,sEAAsE;IACtE,kEAAkE;IAClE,kEAAkE;IAClE,OAAO,IAAIR,QAAQ;QAAEO;QAAOD,OAAOM;IAAgB,GAChDI,kBAAkB,CAAC;QAAEC,KAAK;IAAQ,GAClCC,UAAU,CAACN,iBACXO,WAAW,CAACjB,iBACZkB,WAAW,GACXC,iBAAiB,CAACZ,WAClBa,IAAI,CAACT;AACV"}