@unicitylabs/sphere-sdk 0.7.1 → 0.7.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/README.md +0 -58
- package/dist/core/index.cjs +34 -28
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +15 -19
- package/dist/core/index.d.ts +15 -19
- package/dist/core/index.js +33 -27
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/browser/ipfs.cjs.map +1 -1
- package/dist/impl/browser/ipfs.js.map +1 -1
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +34 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -40
- package/dist/index.d.ts +15 -40
- package/dist/index.js +33 -58
- package/dist/index.js.map +1 -1
- package/dist/l1/index.cjs.map +1 -1
- package/dist/l1/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -159,64 +159,6 @@ sphere.setPriceProvider(createPriceProvider({
|
|
|
159
159
|
}));
|
|
160
160
|
```
|
|
161
161
|
|
|
162
|
-
## Building a Sphere-authenticated backend
|
|
163
|
-
|
|
164
|
-
If your service needs to authenticate users via their Sphere wallet, use `verifySphereAuth`. It performs signature verification and identity derivation in one misuse-resistant call.
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
167
|
-
import { randomBytes } from 'crypto';
|
|
168
|
-
import { verifySphereAuth, AuthVerificationError } from '@unicitylabs/sphere-sdk';
|
|
169
|
-
|
|
170
|
-
const challenges = new Map<string, { challenge: string; expiresAt: number }>();
|
|
171
|
-
|
|
172
|
-
// POST /auth/challenge — issue a one-shot nonce challenge
|
|
173
|
-
server.post('/auth/challenge', async (req) => {
|
|
174
|
-
const { chainPubkey } = req.body;
|
|
175
|
-
const nonce = randomBytes(16).toString('hex');
|
|
176
|
-
const challenge = `Sign in to MyApp\nPubkey: ${chainPubkey}\nNonce: ${nonce}\nIssued At: ${new Date().toISOString()}`;
|
|
177
|
-
challenges.set(chainPubkey, { challenge, expiresAt: Date.now() + 5 * 60_000 });
|
|
178
|
-
return { challenge };
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// POST /auth/verify — turn a signed challenge into a session
|
|
182
|
-
server.post('/auth/verify', async (req, reply) => {
|
|
183
|
-
const { chainPubkey, signature } = req.body;
|
|
184
|
-
const stored = challenges.get(chainPubkey);
|
|
185
|
-
if (!stored || Date.now() > stored.expiresAt) {
|
|
186
|
-
return reply.status(401).send({ error: 'No valid challenge — request a new one' });
|
|
187
|
-
}
|
|
188
|
-
challenges.delete(chainPubkey); // one-shot
|
|
189
|
-
|
|
190
|
-
try {
|
|
191
|
-
const { chainPubkey: pk, directAddress } = await verifySphereAuth({
|
|
192
|
-
challenge: stored.challenge,
|
|
193
|
-
signature,
|
|
194
|
-
chainPubkey,
|
|
195
|
-
});
|
|
196
|
-
// pk and directAddress are now safe to use as user identifiers —
|
|
197
|
-
// neither is a client claim, both are derived from cryptographic proof.
|
|
198
|
-
const token = server.jwt.sign({ sub: pk, addr: directAddress }, { expiresIn: '24h' });
|
|
199
|
-
return { token };
|
|
200
|
-
} catch (err) {
|
|
201
|
-
if (err instanceof AuthVerificationError) {
|
|
202
|
-
return reply.status(401).send({ error: err.message, code: err.code });
|
|
203
|
-
}
|
|
204
|
-
throw err;
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Why not accept `directAddress` from the client?
|
|
210
|
-
|
|
211
|
-
`verifySphereAuth` deliberately does not take a `directAddress` parameter. The signature proves that the client owns the privkey to the pubkey they presented — but it does NOT prove that the pubkey corresponds to any particular `directAddress`. If you accept `directAddress` as a separate body field and use it as the user-identifier key, an attacker can pre-bind any unregistered address to their own pubkey — locking the rightful owner out forever. `verifySphereAuth` derives `directAddress` from `chainPubkey` server-side instead, eliminating the bug class.
|
|
212
|
-
|
|
213
|
-
If you need just the address derivation in a custom flow, use the primitive directly:
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
import { computeDirectAddressFromChainPubkey } from '@unicitylabs/sphere-sdk';
|
|
217
|
-
const addr = await computeDirectAddressFromChainPubkey(chainPubkey);
|
|
218
|
-
```
|
|
219
|
-
|
|
220
162
|
## Testnet Faucet
|
|
221
163
|
|
|
222
164
|
To get test tokens on testnet, you **must first register a nametag**:
|
package/dist/core/index.cjs
CHANGED
|
@@ -881,7 +881,6 @@ __export(core_exports, {
|
|
|
881
881
|
base58Encode: () => base58Encode,
|
|
882
882
|
bytesToHex: () => bytesToHex3,
|
|
883
883
|
checkNetworkHealth: () => checkNetworkHealth,
|
|
884
|
-
computeDirectAddressFromChainPubkey: () => computeDirectAddressFromChainPubkey,
|
|
885
884
|
computeHash160: () => computeHash160,
|
|
886
885
|
convertBits: () => convertBits,
|
|
887
886
|
createAddress: () => createAddress,
|
|
@@ -939,6 +938,7 @@ __export(core_exports, {
|
|
|
939
938
|
randomBytes: () => randomBytes2,
|
|
940
939
|
randomHex: () => randomHex,
|
|
941
940
|
randomUUID: () => randomUUID,
|
|
941
|
+
recoverPubkeyFromSignature: () => recoverPubkeyFromSignature,
|
|
942
942
|
ripemd160: () => ripemd160,
|
|
943
943
|
scanAddressesImpl: () => scanAddressesImpl,
|
|
944
944
|
serializeEncrypted: () => serializeEncrypted,
|
|
@@ -4918,6 +4918,27 @@ function verifySignedMessage(message, signature, expectedPubkey) {
|
|
|
4918
4918
|
return false;
|
|
4919
4919
|
}
|
|
4920
4920
|
}
|
|
4921
|
+
function recoverPubkeyFromSignature(message, signature) {
|
|
4922
|
+
if (signature.length !== 130) {
|
|
4923
|
+
throw new SphereError(
|
|
4924
|
+
`Invalid signature length: expected 130 hex chars, got ${signature.length}`,
|
|
4925
|
+
"SIGNING_ERROR"
|
|
4926
|
+
);
|
|
4927
|
+
}
|
|
4928
|
+
const v = parseInt(signature.slice(0, 2), 16) - 31;
|
|
4929
|
+
const r = signature.slice(2, 66);
|
|
4930
|
+
const s = signature.slice(66, 130);
|
|
4931
|
+
if (v < 0 || v > 3) {
|
|
4932
|
+
throw new SphereError(
|
|
4933
|
+
`Invalid recovery byte: v=${v} out of range [0..3]`,
|
|
4934
|
+
"SIGNING_ERROR"
|
|
4935
|
+
);
|
|
4936
|
+
}
|
|
4937
|
+
const hashHex = hashSignMessage(message);
|
|
4938
|
+
const hashBytes = Buffer.from(hashHex, "hex");
|
|
4939
|
+
const recovered = ec.recoverPubKey(hashBytes, { r, s }, v);
|
|
4940
|
+
return recovered.encode("hex", true);
|
|
4941
|
+
}
|
|
4921
4942
|
|
|
4922
4943
|
// l1/crypto.ts
|
|
4923
4944
|
var import_crypto_js3 = __toESM(require("crypto-js"), 1);
|
|
@@ -27450,42 +27471,27 @@ async function parseAndDecryptWalletDat(data, password, onProgress) {
|
|
|
27450
27471
|
|
|
27451
27472
|
// core/Sphere.ts
|
|
27452
27473
|
var import_SigningService2 = require("@unicitylabs/state-transition-sdk/lib/sign/SigningService");
|
|
27453
|
-
var import_nostr_js_sdk5 = require("@unicitylabs/nostr-js-sdk");
|
|
27454
|
-
|
|
27455
|
-
// core/address-derivation.ts
|
|
27456
27474
|
var import_TokenType5 = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
27457
27475
|
var import_HashAlgorithm7 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
|
|
27458
27476
|
var import_UnmaskedPredicateReference3 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
|
|
27459
|
-
var
|
|
27460
|
-
var COMPRESSED_PUBKEY_RE = /^(02|03)[0-9a-fA-F]{64}$/;
|
|
27461
|
-
async function computeDirectAddressFromChainPubkey(chainPubkey) {
|
|
27462
|
-
if (typeof chainPubkey !== "string" || !COMPRESSED_PUBKEY_RE.test(chainPubkey)) {
|
|
27463
|
-
throw new Error(
|
|
27464
|
-
`computeDirectAddressFromChainPubkey: chainPubkey must be 66-char hex with 02/03 prefix, got "${String(chainPubkey).slice(0, 12)}..."`
|
|
27465
|
-
);
|
|
27466
|
-
}
|
|
27467
|
-
const tokenTypeBytes = Buffer.from(UNICITY_TOKEN_TYPE_HEX2, "hex");
|
|
27468
|
-
const tokenType = new import_TokenType5.TokenType(tokenTypeBytes);
|
|
27469
|
-
const publicKeyBytes = Buffer.from(chainPubkey, "hex");
|
|
27470
|
-
const predicateRef = await import_UnmaskedPredicateReference3.UnmaskedPredicateReference.create(
|
|
27471
|
-
tokenType,
|
|
27472
|
-
"secp256k1",
|
|
27473
|
-
publicKeyBytes,
|
|
27474
|
-
import_HashAlgorithm7.HashAlgorithm.SHA256
|
|
27475
|
-
);
|
|
27476
|
-
return (await predicateRef.toAddress()).toString();
|
|
27477
|
-
}
|
|
27478
|
-
|
|
27479
|
-
// core/Sphere.ts
|
|
27477
|
+
var import_nostr_js_sdk5 = require("@unicitylabs/nostr-js-sdk");
|
|
27480
27478
|
function isValidNametag2(nametag) {
|
|
27481
27479
|
if ((0, import_nostr_js_sdk5.isPhoneNumber)(nametag)) return true;
|
|
27482
27480
|
return /^[a-z0-9_-]{3,20}$/.test(nametag);
|
|
27483
27481
|
}
|
|
27482
|
+
var UNICITY_TOKEN_TYPE_HEX2 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
|
|
27484
27483
|
async function deriveL3PredicateAddress(privateKey) {
|
|
27485
27484
|
const secret = Buffer.from(privateKey, "hex");
|
|
27486
27485
|
const signingService = await import_SigningService2.SigningService.createFromSecret(secret);
|
|
27487
|
-
const
|
|
27488
|
-
|
|
27486
|
+
const tokenTypeBytes = Buffer.from(UNICITY_TOKEN_TYPE_HEX2, "hex");
|
|
27487
|
+
const tokenType = new import_TokenType5.TokenType(tokenTypeBytes);
|
|
27488
|
+
const predicateRef = import_UnmaskedPredicateReference3.UnmaskedPredicateReference.create(
|
|
27489
|
+
tokenType,
|
|
27490
|
+
signingService.algorithm,
|
|
27491
|
+
signingService.publicKey,
|
|
27492
|
+
import_HashAlgorithm7.HashAlgorithm.SHA256
|
|
27493
|
+
);
|
|
27494
|
+
return (await (await predicateRef).toAddress()).toString();
|
|
27489
27495
|
}
|
|
27490
27496
|
var Sphere = class _Sphere {
|
|
27491
27497
|
// Singleton
|
|
@@ -30870,7 +30876,6 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
|
30870
30876
|
base58Encode,
|
|
30871
30877
|
bytesToHex,
|
|
30872
30878
|
checkNetworkHealth,
|
|
30873
|
-
computeDirectAddressFromChainPubkey,
|
|
30874
30879
|
computeHash160,
|
|
30875
30880
|
convertBits,
|
|
30876
30881
|
createAddress,
|
|
@@ -30928,6 +30933,7 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
|
30928
30933
|
randomBytes,
|
|
30929
30934
|
randomHex,
|
|
30930
30935
|
randomUUID,
|
|
30936
|
+
recoverPubkeyFromSignature,
|
|
30931
30937
|
ripemd160,
|
|
30932
30938
|
scanAddressesImpl,
|
|
30933
30939
|
serializeEncrypted,
|