@soliton-id/web-sdk 1.0.0 → 1.0.2
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 +31 -0
- package/README.md +127 -40
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,37 @@ All notable changes to `@soliton-id/web-sdk`. The `soliton-v1` postMessage
|
|
|
4
4
|
protocol is frozen: changes are additive only, so older SDK versions keep
|
|
5
5
|
working against the hosted flow.
|
|
6
6
|
|
|
7
|
+
## 1.0.2 — 2026-08-06
|
|
8
|
+
|
|
9
|
+
Documentation only. No runtime, API, or bundle changes; `soliton-v1` is
|
|
10
|
+
unchanged and `1.0.0`/`1.0.1` remain fully compatible.
|
|
11
|
+
|
|
12
|
+
- Reframed `sk_test_` as the **sandbox environment** rather than a
|
|
13
|
+
fixtures-only mode. A sandbox credential now runs real captures; the
|
|
14
|
+
deterministic `pass` / `fail` / `partial` fixtures remain available and
|
|
15
|
+
remain sandbox-only. `sk_live_` still cannot request a fixture.
|
|
16
|
+
- Documented that redirect domains, iframe origins, and webhook endpoints are
|
|
17
|
+
registered per environment, so a staging origin can embed sandbox links
|
|
18
|
+
without being able to embed production ones, and each environment's webhook
|
|
19
|
+
has its own signing secret.
|
|
20
|
+
- Documented the additive `environment` field on the create response, every
|
|
21
|
+
status read, and every webhook payload.
|
|
22
|
+
|
|
23
|
+
## 1.0.1 — 2026-08-02
|
|
24
|
+
|
|
25
|
+
Documentation only. No runtime, API, or bundle changes; `soliton-v1` is
|
|
26
|
+
unchanged and `1.0.0` remains fully compatible. Published because npm's
|
|
27
|
+
`1.0.0` README is immutable and described test keys incorrectly.
|
|
28
|
+
|
|
29
|
+
- Rewrote the test-key section: `sk_test_` credentials now produce
|
|
30
|
+
deterministic synthetic fixtures (`pass` / `fail` / `partial`) that never
|
|
31
|
+
open the camera, rather than running the same real capture pipeline as
|
|
32
|
+
`sk_live_`. Documented the scenario/verdict mapping and the strict
|
|
33
|
+
live/test read isolation.
|
|
34
|
+
- Documented completion webhooks as a polling alternative, and corrected the
|
|
35
|
+
iframe-origin, CSP, and `Permissions-Policy` guidance to describe the
|
|
36
|
+
iframe's direct-parent origin and nested frame chains.
|
|
37
|
+
|
|
7
38
|
## 1.0.0 — 2026-08-01
|
|
8
39
|
|
|
9
40
|
First public release.
|
package/README.md
CHANGED
|
@@ -92,7 +92,7 @@ BACKEND with your API key:
|
|
|
92
92
|
user, selected server-side from the capture under neutral lighting.
|
|
93
93
|
Returns 404 until the verification is complete or when no acceptable
|
|
94
94
|
portrait frame existed. Right after completion, `portrait_available` can
|
|
95
|
-
briefly read `false` while extraction finishes; once status is `
|
|
95
|
+
briefly read `false` while extraction finishes; once status is `completed`
|
|
96
96
|
just fetch the portrait and treat a 404 after one short retry as final.
|
|
97
97
|
|
|
98
98
|
Your page and the SDK never see the verdict or the portrait: callbacks and
|
|
@@ -120,29 +120,55 @@ creation and check it on every status read to make this auditable.
|
|
|
120
120
|
|
|
121
121
|
Embedding is off by default and gated on BOTH layers:
|
|
122
122
|
|
|
123
|
-
1. Your
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
123
|
+
1. Your Soliton account must have the exact origin of each page that runs the
|
|
124
|
+
SDK and directly contains its Soliton iframe registered (scheme + host +
|
|
125
|
+
port, e.g. `https://app.example.com`), **for the environment that will open
|
|
126
|
+
the link**. This direct-parent page does not have to be the browser's
|
|
127
|
+
top-level page. A link is checked against the origins registered for the
|
|
128
|
+
environment of the key that created it, so `https://dev.example.com` can be
|
|
129
|
+
a sandbox embedder without becoming a production one.
|
|
130
|
+
2. The direct-parent origin must also be present in the soliton.id deploy-time
|
|
131
|
+
`frame-ancestors` allowlist. If the SDK page is itself framed, every origin
|
|
132
|
+
above it in the frame chain must be present there too (we manage this
|
|
133
|
+
allowlist). This layer is one list for the whole deployment and is not per
|
|
134
|
+
environment; the per-environment precision comes from layer 1.
|
|
135
|
+
|
|
136
|
+
A verification link opened inside an iframe with any other direct-parent
|
|
137
|
+
origin refuses to start and reports `origin_not_allowed`.
|
|
131
138
|
|
|
132
139
|
During the invite-only pilot, Soliton manually manages both layers. Send your
|
|
133
|
-
Soliton contact every exact production
|
|
134
|
-
testing.
|
|
140
|
+
Soliton contact every exact production and local direct-parent origin before
|
|
141
|
+
testing. Include a staging origin only when Soliton has supplied a working
|
|
142
|
+
staging flow URL. For a nested integration, also send every other ancestor
|
|
143
|
+
origin. Wildcards are not accepted, and adding or changing an origin can
|
|
135
144
|
require a Soliton deployment.
|
|
136
145
|
|
|
146
|
+
Register the direct-parent page's origin, not a URL path. For example,
|
|
147
|
+
`https://app.example.com` and `https://app.example.com:8443` are different
|
|
148
|
+
origins; `/verify`, a query string, and a trailing slash are not part of the
|
|
149
|
+
value you send us. Redirect-domain registration and iframe-origin registration
|
|
150
|
+
are separate: enabling one does not enable the other.
|
|
151
|
+
|
|
137
152
|
## Customer security headers
|
|
138
153
|
|
|
139
|
-
|
|
140
|
-
sources
|
|
141
|
-
|
|
154
|
+
Content Security Policy (CSP) is the browser response header that controls
|
|
155
|
+
which sources a page may load. If the page that runs the SDK sends restrictive
|
|
156
|
+
security headers, merge the Soliton sources into its existing policies; do not
|
|
157
|
+
replace the rest of your policy with these minimal examples.
|
|
158
|
+
|
|
159
|
+
When you bundle the npm package:
|
|
160
|
+
|
|
161
|
+
```text
|
|
162
|
+
Content-Security-Policy: frame-src https://soliton.id
|
|
163
|
+
Permissions-Policy: camera=(self "https://soliton.id"), microphone=(self "https://soliton.id")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
When you load the classic CDN script or hosted ESM build, the remote script
|
|
167
|
+
also needs to be allowed:
|
|
142
168
|
|
|
143
169
|
```text
|
|
144
|
-
Content-Security-Policy: frame-src https://soliton.id
|
|
145
|
-
Permissions-Policy: camera=(self "https://soliton.id"
|
|
170
|
+
Content-Security-Policy: frame-src https://soliton.id; script-src https://soliton.id
|
|
171
|
+
Permissions-Policy: camera=(self "https://soliton.id"), microphone=(self "https://soliton.id")
|
|
146
172
|
```
|
|
147
173
|
|
|
148
174
|
- `frame-src` must include the Soliton origin you use because the capture flow
|
|
@@ -150,31 +176,92 @@ Permissions-Policy: camera=(self "https://soliton.id" "https://staging.soliton.i
|
|
|
150
176
|
- `script-src` must include the Soliton origin you use only for the classic
|
|
151
177
|
script or hosted ESM distribution. It is not needed when your application
|
|
152
178
|
bundles the npm package.
|
|
153
|
-
- If
|
|
154
|
-
microphone
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
179
|
+
- If any document above the Soliton iframe explicitly restricts camera or
|
|
180
|
+
microphone with `Permissions-Policy`, every level in a nested frame chain
|
|
181
|
+
must allow and delegate both features through each intervening iframe; an
|
|
182
|
+
upstream denial cannot be overridden lower in the chain. The SDK already
|
|
183
|
+
sets the matching `allow="camera; microphone"` attribute on the iframe it
|
|
184
|
+
creates.
|
|
185
|
+
|
|
186
|
+
These examples use production. Add a staging origin to these directives only
|
|
187
|
+
when Soliton has supplied its exact, working staging URL; include both origins
|
|
188
|
+
only if the same page can open both environments.
|
|
189
|
+
|
|
190
|
+
For local customer-page testing, send us the exact origin, including its port
|
|
191
|
+
(for example `http://localhost:3000`), for registration. It does not belong in
|
|
192
|
+
`frame-src` or `script-src` while the iframe and hosted SDK still load from
|
|
193
|
+
soliton.id. Do not add Soliton to your page's `frame-ancestors`: that directive
|
|
194
|
+
controls who may frame your page, while `frame-src` controls what your page may
|
|
195
|
+
frame.
|
|
196
|
+
|
|
197
|
+
## Sandbox
|
|
198
|
+
|
|
199
|
+
Your account has two environments and the API key prefix selects one:
|
|
200
|
+
`sk_live_` is **production**, `sk_test_` is **sandbox**. Each has its own
|
|
201
|
+
credentials, verifications, redirect domains, iframe origins, and webhook
|
|
202
|
+
endpoint with its own signing secret.
|
|
203
|
+
|
|
204
|
+
A sandbox verification opens in the overlay exactly like a production one and
|
|
205
|
+
runs the same real capture — but it is checked against the iframe origins
|
|
206
|
+
registered for **sandbox**, so your staging page can embed sandbox links
|
|
207
|
+
without being able to embed production ones. Register each origin for the
|
|
208
|
+
environment that will use it.
|
|
209
|
+
|
|
210
|
+
Sandbox is additionally the only place that can request a fixed outcome, which
|
|
211
|
+
is the practical way to exercise your `partial` branch:
|
|
212
|
+
|
|
213
|
+
```jsonc
|
|
214
|
+
// POST /api/v1/verifications with an sk_test_ key
|
|
215
|
+
{ "client_reference_id": "user-8412", "test_fixture_scenario": "partial" }
|
|
216
|
+
```
|
|
162
217
|
|
|
163
|
-
`
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
218
|
+
| `test_fixture_scenario` | `verified` | `partial_verification` |
|
|
219
|
+
| ----------------------- | ---------- | ---------------------- |
|
|
220
|
+
| `pass` | `true` | `false` |
|
|
221
|
+
| `partial` | `true` | `true` |
|
|
222
|
+
| `fail` | `false` | `false` |
|
|
223
|
+
|
|
224
|
+
A fixture's URL opens in the overlay and fires `onComplete` the same way, but
|
|
225
|
+
never requests camera permission and never records anything. The hosted page
|
|
226
|
+
marks itself as synthetic on screen.
|
|
227
|
+
|
|
228
|
+
Fixtures do not exercise liveness, anti-spoof, or anti-deepfake logic, and
|
|
229
|
+
simulate no errors or timeouts — only the three terminal verdicts. Real
|
|
230
|
+
sandbox captures do run the full pipeline; a production `sk_live_` key cannot
|
|
231
|
+
request fixtures at all.
|
|
232
|
+
|
|
233
|
+
The two environments are strictly isolated: neither credential can read the
|
|
234
|
+
other's verifications, even within one account. Every create response, status
|
|
235
|
+
read, and webhook carries `environment` (`sandbox` or `production`) and
|
|
236
|
+
`result_source` (`biometric` or `synthetic_test_fixture`) — assert both
|
|
237
|
+
anywhere a real production verdict matters.
|
|
238
|
+
|
|
239
|
+
## Completion webhooks
|
|
240
|
+
|
|
241
|
+
Instead of polling, your server can receive a signed `verification.completed`
|
|
242
|
+
POST the moment a verification reaches its final verdict. Like `onComplete`, it
|
|
243
|
+
is a "check now" signal rather than the verdict itself — fetch the verification
|
|
244
|
+
server-to-server before acting on it. Verify the `X-Soliton-Signature` HMAC,
|
|
245
|
+
deduplicate on `X-Soliton-Event-Id`, and respond `2xx` within 5 seconds. Full
|
|
246
|
+
setup, the signature recipe, and retry semantics are in the
|
|
247
|
+
[integration guide](https://soliton.id/docs/integration).
|
|
248
|
+
|
|
249
|
+
Webhooks are configured per environment: one endpoint for sandbox and one for
|
|
250
|
+
production, each with its own signing secret. A sandbox completion is only ever
|
|
251
|
+
delivered to the sandbox endpoint, so a staging receiver cannot validate — or
|
|
252
|
+
forge — a production event. The payload's `environment` field tells you which
|
|
253
|
+
is which if you point both at one URL.
|
|
168
254
|
|
|
169
255
|
## How it works
|
|
170
256
|
|
|
171
|
-
`open()` accepts hosted URLs
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
257
|
+
`open()` accepts hosted URLs from `https://soliton.id`. When Soliton has
|
|
258
|
+
supplied a working staging flow URL, it also accepts
|
|
259
|
+
`https://staging.soliton.id`; HTTP localhost/127.0.0.1 URLs are accepted for
|
|
260
|
+
development. Every URL must use a `/v/{token}` path; query parameters are
|
|
261
|
+
preserved. The SDK renders a full-viewport overlay with an iframe pointing at
|
|
262
|
+
that validated origin and listens for the hosted page's lifecycle messages.
|
|
263
|
+
Every message is checked three ways (origin, source window, protocol marker)
|
|
264
|
+
before it reaches your callbacks, and unknown message types are ignored, so
|
|
265
|
+
wrapper versions stay compatible as the hosted flow evolves. Redirect-style
|
|
266
|
+
integration (no SDK, we redirect the user back to you) remains fully
|
|
267
|
+
supported; the same verification URL works in both modes.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"//": "Publishable manifest for the npm Web SDK. No devDependencies and no own node_modules by design: every build runs from the next-app root with its existing toolchain (npm run build:sdk), so the lockfile never grows for packaging. dist/ is rebuilt automatically by the prepack hook and is not committed.",
|
|
3
3
|
"name": "@soliton-id/web-sdk",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"description": "Soliton identity verification for the browser: opens the hosted verification flow in a full-viewport overlay iframe on your page and reports lifecycle events. Verdicts and portraits are fetched by your backend, never exposed in the browser.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Soliton",
|