Files
node-red-contrib-matrix-chat/examples/device-verification-flow.json
T
skylord123 ebcb1eab81 Upgrade to matrix-js-sdk 41.5.0; add device verification
Upgrades matrix-js-sdk from 34.13.0 to 41.5.0. This crosses the v37
removal of the legacy libolm crypto stack, so E2EE is migrated to the
Rust crypto implementation. Also adds device verification, cross-signing
setup, and authenticated media support.

Dependencies
- Bump matrix-js-sdk ^34.13.0 -> ^41.5.0; require Node.js >= 22.
- Drop the `olm` dependency (legacy crypto only); add `fake-indexeddb`.

Rust crypto
- Replace initCrypto() with initRustCrypto(); the legacy crypto stack
  was removed upstream in v37.
- Add src/matrix-crypto-store.js: the Rust crypto store requires
  IndexedDB, absent in Node.js, so it is backed by fake-indexeddb and
  snapshotted to disk (rust-crypto-store.v8) to survive restarts.
- Migrate existing libolm crypto state into the Rust store on first run,
  and discard the stored crypto state when the device ID changes.

Homeserver discovery
- Resolve the homeserver via .well-known, so a delegating domain
  (e.g. example.org) works as the configured server URL.

Cross-signing & secure backup
- Add a secured /matrix-chat/secure-backup admin endpoint and a modal
  dialog on the server config node: check status, unlock an existing
  secure backup with its recovery key, or reset and create a new one.

Device verification (new nodes)
- matrix-verification: event source emitting verification requests and
  phase changes, with on-node filters (phase, initiated by, type,
  self-verification, user allowlist, room).
- matrix-verification-action: request, accept, start SAS, confirm,
  mismatch, or cancel an in-flight verification.

Authenticated media
- matrix-receive and matrix-crypt-file use the authenticated media
  endpoints, send a bearer token via msg.headers, and fall back between
  the v3 and v1 media endpoints on a 404.

Fixes
- Surface connection/auth errors in the log; node.error() calls were
  passed an empty msg object, which routed the error and suppressed
  console logging.
- matrix-get-user: await getProfileInfo()/getPresence().
- matrix-invite-room: pass the reason as the third invite() argument
  (the removed callback parameter was shifting it out).
- Guard the verification handlers so a throwing SDK getter cannot crash
  Node-RED.

Docs
- Add the device-verification example flow; update the READMEs and node
  help, correcting stale claims that device verification, secure backup,
  and encrypted file uploads were unsupported.
2026-05-22 14:40:00 -06:00

548 lines
9.3 KiB
JSON

[
{
"id": "7158964bd67edc52",
"type": "group",
"z": "vtest",
"name": "Example verification flow",
"style": {
"label": true
},
"nodes": [
"40c105c38054d6db",
"83f785d52a61009a",
"d51bab8cbf5f247c",
"2e543533d49b467c"
],
"x": 88,
"y": 73,
"w": 1044,
"h": 754
},
{
"id": "40c105c38054d6db",
"type": "group",
"z": "vtest",
"g": "7158964bd67edc52",
"name": "Verification request handling",
"style": {
"label": true
},
"nodes": [
"mv_all",
"dbg_events",
"sw_phase",
"act_accept",
"act_start",
"chg_savevid",
"dbg_sas",
"dbg_done",
"dbg_cancelled",
"dbg_err"
],
"x": 114,
"y": 99,
"w": 992,
"h": 342
},
{
"id": "mv_all",
"type": "matrix-verification",
"z": "vtest",
"g": "40c105c38054d6db",
"name": "All verifications",
"server": null,
"phaseRequested": true,
"phaseReady": true,
"phaseStarted": true,
"phaseSas": true,
"phaseDone": true,
"phaseCancelled": true,
"initiatedBy": "any",
"verificationType": "any",
"selfVerification": "any",
"userFilter": "",
"roomFilter": "",
"x": 220,
"y": 180,
"wires": [
[
"dbg_events",
"sw_phase"
]
]
},
{
"id": "dbg_events",
"type": "debug",
"z": "vtest",
"g": "40c105c38054d6db",
"name": "all verification events",
"active": true,
"tosidebar": true,
"complete": "true",
"x": 500,
"y": 140,
"wires": []
},
{
"id": "sw_phase",
"type": "switch",
"z": "vtest",
"g": "40c105c38054d6db",
"name": "route by phase",
"property": "phase",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "requested",
"vt": "str"
},
{
"t": "eq",
"v": "ready",
"vt": "str"
},
{
"t": "eq",
"v": "sas",
"vt": "str"
},
{
"t": "eq",
"v": "done",
"vt": "str"
},
{
"t": "eq",
"v": "cancelled",
"vt": "str"
}
],
"checkall": "true",
"outputs": 5,
"x": 460,
"y": 220,
"wires": [
[
"act_accept"
],
[
"act_start"
],
[
"chg_savevid"
],
[
"dbg_done"
],
[
"dbg_cancelled"
]
]
},
{
"id": "act_accept",
"type": "matrix-verification-action",
"z": "vtest",
"g": "40c105c38054d6db",
"name": "Accept",
"server": null,
"mode": "accept",
"x": 700,
"y": 180,
"wires": [
[],
[
"dbg_err"
]
]
},
{
"id": "act_start",
"type": "matrix-verification-action",
"z": "vtest",
"g": "40c105c38054d6db",
"name": "Start SAS",
"server": null,
"mode": "start",
"x": 700,
"y": 230,
"wires": [
[],
[
"dbg_err"
]
]
},
{
"id": "chg_savevid",
"type": "change",
"z": "vtest",
"g": "40c105c38054d6db",
"name": "save verificationId",
"rules": [
{
"t": "set",
"p": "verificationId",
"pt": "flow",
"to": "verificationId",
"tot": "msg"
}
],
"x": 710,
"y": 290,
"wires": [
[
"dbg_sas"
]
]
},
{
"id": "dbg_sas",
"type": "debug",
"z": "vtest",
"g": "40c105c38054d6db",
"name": "SAS emoji (msg.sas)",
"active": true,
"tosidebar": true,
"complete": "true",
"x": 960,
"y": 290,
"wires": []
},
{
"id": "dbg_done",
"type": "debug",
"z": "vtest",
"g": "40c105c38054d6db",
"name": "verification done",
"active": true,
"tosidebar": true,
"complete": "true",
"x": 710,
"y": 350,
"wires": []
},
{
"id": "dbg_cancelled",
"type": "debug",
"z": "vtest",
"g": "40c105c38054d6db",
"name": "verification cancelled",
"active": true,
"tosidebar": true,
"complete": "true",
"x": 730,
"y": 400,
"wires": []
},
{
"id": "dbg_err",
"type": "debug",
"z": "vtest",
"g": "40c105c38054d6db",
"name": "action errors",
"active": true,
"tosidebar": true,
"complete": "true",
"x": 940,
"y": 200,
"wires": []
},
{
"id": "83f785d52a61009a",
"type": "group",
"z": "vtest",
"g": "7158964bd67edc52",
"name": "Confirm or reject last verification request",
"style": {
"label": true
},
"nodes": [
"inj_confirm",
"chg_vid_c",
"act_confirm",
"inj_reject",
"chg_vid_r",
"act_mismatch",
"dbg_result"
],
"x": 114,
"y": 459,
"w": 982,
"h": 142
},
{
"id": "inj_confirm",
"type": "inject",
"z": "vtest",
"g": "83f785d52a61009a",
"name": "Confirm SAS match",
"props": [],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"x": 250,
"y": 500,
"wires": [
[
"chg_vid_c"
]
]
},
{
"id": "chg_vid_c",
"type": "change",
"z": "vtest",
"g": "83f785d52a61009a",
"name": "verificationId from flow",
"rules": [
{
"t": "set",
"p": "verificationId",
"pt": "msg",
"to": "verificationId",
"tot": "flow"
}
],
"x": 500,
"y": 500,
"wires": [
[
"act_confirm"
]
]
},
{
"id": "act_confirm",
"type": "matrix-verification-action",
"z": "vtest",
"g": "83f785d52a61009a",
"name": "Confirm SAS",
"server": null,
"mode": "confirm",
"x": 750,
"y": 500,
"wires": [
[
"dbg_result"
],
[
"dbg_err"
]
]
},
{
"id": "inj_reject",
"type": "inject",
"z": "vtest",
"g": "83f785d52a61009a",
"name": "Reject SAS (mismatch)",
"props": [],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"x": 260,
"y": 560,
"wires": [
[
"chg_vid_r"
]
]
},
{
"id": "chg_vid_r",
"type": "change",
"z": "vtest",
"g": "83f785d52a61009a",
"name": "verificationId from flow",
"rules": [
{
"t": "set",
"p": "verificationId",
"pt": "msg",
"to": "verificationId",
"tot": "flow"
}
],
"x": 500,
"y": 560,
"wires": [
[
"act_mismatch"
]
]
},
{
"id": "act_mismatch",
"type": "matrix-verification-action",
"z": "vtest",
"g": "83f785d52a61009a",
"name": "Reject (mismatch)",
"server": null,
"mode": "mismatch",
"x": 760,
"y": 560,
"wires": [
[
"dbg_result"
],
[
"dbg_err"
]
]
},
{
"id": "dbg_result",
"type": "debug",
"z": "vtest",
"g": "83f785d52a61009a",
"name": "action result",
"active": true,
"tosidebar": true,
"complete": "true",
"x": 980,
"y": 530,
"wires": []
},
{
"id": "d51bab8cbf5f247c",
"type": "group",
"z": "vtest",
"g": "7158964bd67edc52",
"name": "Request verification with specific user & device",
"style": {
"label": true
},
"nodes": [
"inj_request",
"act_request"
],
"x": 114,
"y": 619,
"w": 512,
"h": 82
},
{
"id": "inj_request",
"type": "inject",
"z": "vtest",
"g": "d51bab8cbf5f247c",
"name": "Verify a user & device",
"props": [
{
"p": "userId",
"v": "@CHANGE_ME:example.org",
"vt": "str"
},
{
"p": "deviceId",
"v": "CHANGE_ME",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"x": 260,
"y": 660,
"wires": [
[
"act_request"
]
]
},
{
"id": "act_request",
"type": "matrix-verification-action",
"z": "vtest",
"g": "d51bab8cbf5f247c",
"name": "Request verification",
"server": null,
"mode": "request",
"x": 510,
"y": 660,
"wires": [
[
"dbg_result"
],
[
"dbg_err"
]
]
},
{
"id": "2e543533d49b467c",
"type": "group",
"z": "vtest",
"g": "7158964bd67edc52",
"name": "Request verification with specific user & room",
"style": {
"label": true
},
"nodes": [
"f7c043d39780b9a4",
"b2807fd5125b56b4"
],
"x": 114,
"y": 719,
"w": 512,
"h": 82
},
{
"id": "f7c043d39780b9a4",
"type": "inject",
"z": "vtest",
"g": "2e543533d49b467c",
"name": "Verify a user & room",
"props": [
{
"p": "userId",
"v": "@CHANGE_ME:example.org",
"vt": "str"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "CHANGE_ME",
"x": 250,
"y": 760,
"wires": [
[
"b2807fd5125b56b4"
]
]
},
{
"id": "b2807fd5125b56b4",
"type": "matrix-verification-action",
"z": "vtest",
"g": "2e543533d49b467c",
"name": "Request verification",
"server": null,
"mode": "request",
"x": 510,
"y": 760,
"wires": [
[
"dbg_result"
],
[
"dbg_err"
]
]
}
]