fix(api): sign invites with stored owner keypair instead of unsigned placeholder
Some checks failed
CI / Tests / 🧪 Test (push) Has been cancelled
Some checks failed
CI / Tests / 🧪 Test (push) Has been cancelled
Production /join on the broker (from feat 18c) rejects every invite
with invite_bad_signature because the web UI was emitting unsigned
payloads. This fixes that.
createMyMesh now generates ed25519 owner keypair + 32-byte root key
and stores all three on the mesh row. createMyInvite loads them,
signs the canonical invite bytes via crypto_sign_detached, and
emits a fully-signed payload matching what the broker expects:
payload = {v, mesh_id, mesh_slug, broker_url, expires_at,
mesh_root_key, role, owner_pubkey, signature}
canonical = same fields minus signature, "|"-delimited
signature = ed25519_sign(canonical, mesh.owner_secret_key)
token = base64url(JSON(payload)) ← stored as invite.token
The base64url(JSON) token IS the DB lookup key — broker's /join
does `WHERE invite.token = <that string>`, then re-verifies the
signature it extracts from the decoded payload.
Also drops the sha256 derivePlaceholderRootKey() helper and the
encodeInviteLink helper, both replaced by inline logic.
backfill extended: the one-off script now populates owner_pubkey
AND owner_secret_key AND root_key together in a single pass. Query
condition is `WHERE any of the three IS NULL`, so running it
post-migration catches every row regardless of partial prior fills.
requires packages/api to depend on libsodium-wrappers + types
(added). 64/64 broker tests still green.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
57
pnpm-lock.yaml
generated
57
pnpm-lock.yaml
generated
@@ -302,6 +302,9 @@ importers:
|
||||
pdfjs-dist:
|
||||
specifier: 5.4.530
|
||||
version: 5.4.530
|
||||
qrcode:
|
||||
specifier: 1.5.4
|
||||
version: 1.5.4
|
||||
react:
|
||||
specifier: catalog:react19
|
||||
version: 19.1.0
|
||||
@@ -363,6 +366,9 @@ importers:
|
||||
'@types/node':
|
||||
specifier: catalog:node22
|
||||
version: 22.16.0
|
||||
'@types/qrcode':
|
||||
specifier: 1.5.6
|
||||
version: 1.5.6
|
||||
'@types/react':
|
||||
specifier: 19.2.7
|
||||
version: 19.2.7
|
||||
@@ -545,6 +551,9 @@ importers:
|
||||
hono:
|
||||
specifier: 4.10.4
|
||||
version: 4.10.4
|
||||
libsodium-wrappers:
|
||||
specifier: 0.7.15
|
||||
version: 0.7.15
|
||||
zod:
|
||||
specifier: 'catalog:'
|
||||
version: 4.1.13
|
||||
@@ -561,6 +570,9 @@ importers:
|
||||
'@turbostarter/vitest-config':
|
||||
specifier: workspace:*
|
||||
version: link:../../tooling/vitest
|
||||
'@types/libsodium-wrappers':
|
||||
specifier: 0.7.14
|
||||
version: 0.7.14
|
||||
eslint:
|
||||
specifier: 'catalog:'
|
||||
version: 9.39.0(jiti@2.6.1)
|
||||
@@ -7046,6 +7058,9 @@ packages:
|
||||
'@types/prismjs@1.26.5':
|
||||
resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==}
|
||||
|
||||
'@types/qrcode@1.5.6':
|
||||
resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==}
|
||||
|
||||
'@types/react-dom@19.0.4':
|
||||
resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==}
|
||||
peerDependencies:
|
||||
@@ -8424,6 +8439,9 @@ packages:
|
||||
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
|
||||
dijkstrajs@1.0.3:
|
||||
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
|
||||
|
||||
dir-glob@3.0.1:
|
||||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -11371,6 +11389,10 @@ packages:
|
||||
resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
|
||||
pngjs@5.0.0:
|
||||
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
possible-typed-array-names@1.1.0:
|
||||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -11654,6 +11676,11 @@ packages:
|
||||
resolution: {integrity: sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==}
|
||||
hasBin: true
|
||||
|
||||
qrcode@1.5.4:
|
||||
resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
hasBin: true
|
||||
|
||||
qs@6.14.0:
|
||||
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
|
||||
engines: {node: '>=0.6'}
|
||||
@@ -15888,7 +15915,7 @@ snapshots:
|
||||
ci-info: 3.9.0
|
||||
compression: 1.8.0
|
||||
connect: 3.7.0
|
||||
debug: 4.4.1
|
||||
debug: 4.4.3
|
||||
env-editor: 0.4.2
|
||||
expo: 54.0.27(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.17)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3)
|
||||
expo-server: 1.0.5
|
||||
@@ -15945,7 +15972,7 @@ snapshots:
|
||||
'@expo/plist': 0.4.8
|
||||
'@expo/sdk-runtime-versions': 1.0.0
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.1
|
||||
debug: 4.4.3
|
||||
getenv: 2.0.0
|
||||
glob: 13.0.0
|
||||
resolve-from: 5.0.0
|
||||
@@ -15999,7 +16026,7 @@ snapshots:
|
||||
'@expo/env@2.0.8':
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.1
|
||||
debug: 4.4.3
|
||||
dotenv: 16.4.7
|
||||
dotenv-expand: 11.0.7
|
||||
getenv: 2.0.0
|
||||
@@ -16012,7 +16039,7 @@ snapshots:
|
||||
'@expo/spawn-async': 1.7.2
|
||||
arg: 5.0.2
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.1
|
||||
debug: 4.4.3
|
||||
getenv: 2.0.0
|
||||
glob: 13.0.0
|
||||
ignore: 5.3.2
|
||||
@@ -16056,7 +16083,7 @@ snapshots:
|
||||
'@expo/spawn-async': 1.7.2
|
||||
browserslist: 4.25.1
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.1
|
||||
debug: 4.4.3
|
||||
dotenv: 16.4.7
|
||||
dotenv-expand: 11.0.7
|
||||
getenv: 2.0.0
|
||||
@@ -16139,7 +16166,7 @@ snapshots:
|
||||
'@expo/image-utils': 0.8.8
|
||||
'@expo/json-file': 10.0.8
|
||||
'@react-native/normalize-colors': 0.81.5
|
||||
debug: 4.4.1
|
||||
debug: 4.4.3
|
||||
expo: 54.0.27(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.17)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3)
|
||||
resolve-from: 5.0.0
|
||||
semver: 7.7.2
|
||||
@@ -20534,6 +20561,10 @@ snapshots:
|
||||
|
||||
'@types/prismjs@1.26.5': {}
|
||||
|
||||
'@types/qrcode@1.5.6':
|
||||
dependencies:
|
||||
'@types/node': 24.0.13
|
||||
|
||||
'@types/react-dom@19.0.4(@types/react@19.2.7)':
|
||||
dependencies:
|
||||
'@types/react': 19.2.7
|
||||
@@ -21305,7 +21336,7 @@ snapshots:
|
||||
babel-plugin-react-native-web: 0.21.1
|
||||
babel-plugin-syntax-hermes-parser: 0.29.1
|
||||
babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.28.5)
|
||||
debug: 4.4.1
|
||||
debug: 4.4.3
|
||||
react-refresh: 0.14.2
|
||||
resolve-from: 5.0.0
|
||||
optionalDependencies:
|
||||
@@ -22060,6 +22091,8 @@ snapshots:
|
||||
|
||||
diff@4.0.2: {}
|
||||
|
||||
dijkstrajs@1.0.3: {}
|
||||
|
||||
dir-glob@3.0.1:
|
||||
dependencies:
|
||||
path-type: 4.0.0
|
||||
@@ -22850,7 +22883,7 @@ snapshots:
|
||||
'@react-navigation/native': 7.1.14(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3)
|
||||
'@react-navigation/native-stack': 7.3.21(@react-navigation/native@7.1.14(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3)
|
||||
client-only: 0.0.1
|
||||
debug: 4.4.1
|
||||
debug: 4.4.3
|
||||
escape-string-regexp: 4.0.0
|
||||
expo: 54.0.27(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.17)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3)
|
||||
expo-constants: 18.0.11(expo@54.0.27)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.2.3))
|
||||
@@ -25815,6 +25848,8 @@ snapshots:
|
||||
pngjs@3.4.0:
|
||||
optional: true
|
||||
|
||||
pngjs@5.0.0: {}
|
||||
|
||||
possible-typed-array-names@1.1.0: {}
|
||||
|
||||
postcss-import@15.1.0(postcss@8.5.6):
|
||||
@@ -26028,6 +26063,12 @@ snapshots:
|
||||
qrcode-terminal@0.11.0:
|
||||
optional: true
|
||||
|
||||
qrcode@1.5.4:
|
||||
dependencies:
|
||||
dijkstrajs: 1.0.3
|
||||
pngjs: 5.0.0
|
||||
yargs: 15.4.1
|
||||
|
||||
qs@6.14.0:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
|
||||
Reference in New Issue
Block a user