@nevermined-io/ui-widgets-server 0.5.7 → 0.5.9
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 +33 -31
- package/dist/create-widget-session.d.ts +12 -0
- package/dist/create-widget-session.d.ts.map +1 -0
- package/dist/create-widget-session.js +57 -0
- package/dist/create-widget-session.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +27 -18
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -4
- package/dist/create-init-token.d.ts +0 -3
- package/dist/create-init-token.d.ts.map +0 -1
- package/dist/create-init-token.js +0 -22
- package/dist/create-init-token.js.map +0 -1
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
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
23
|
+
import { createWidgetSession } from '@nevermined-io/ui-widgets-server'
|
|
24
24
|
|
|
25
25
|
const app = express()
|
|
26
26
|
|
|
27
|
-
app.get('/api/widget-
|
|
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
|
|
34
|
+
const session = await createWidgetSession({
|
|
35
35
|
email,
|
|
36
36
|
orgId: process.env.NVM_ORG_ID!,
|
|
37
|
-
|
|
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(
|
|
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
|
|
50
|
+
const session = await fetch('/api/widget-session').then((r) => r.json())
|
|
50
51
|
|
|
51
52
|
const nvm = await NeverminedWidgets.initialize({
|
|
52
|
-
|
|
53
|
+
session,
|
|
53
54
|
environment: 'sandbox',
|
|
54
55
|
})
|
|
55
56
|
```
|
|
56
57
|
|
|
57
58
|
## API
|
|
58
59
|
|
|
59
|
-
### `
|
|
60
|
+
### `createWidgetSession(options): Promise<WidgetSession>`
|
|
60
61
|
|
|
61
|
-
|
|
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
|
|
66
|
-
| ------------ |
|
|
67
|
-
| `email` | `string`
|
|
68
|
-
| `orgId` | `string`
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
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
|
-
####
|
|
74
|
+
#### Response shape
|
|
73
75
|
|
|
74
76
|
```ts
|
|
75
|
-
interface
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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.
|
|
86
|
+
#### Breaking change vs `< 0.6.0`
|
|
86
87
|
|
|
87
|
-
|
|
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 `
|
|
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 {
|
|
2
|
-
export type {
|
|
1
|
+
export { createWidgetSession } from './create-widget-session.js';
|
|
2
|
+
export type { CreateWidgetSessionOptions, WidgetSession } from './types.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
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
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {
|
|
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
|
|
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
|
|
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
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,
|
|
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
|
|
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.
|
|
3
|
+
"version": "0.5.9",
|
|
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 +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"}
|