@thurinlabs/identity-kit 1.0.2 → 1.0.4
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/LICENSE +21 -0
- package/README.md +4 -0
- package/dist/embed.global.js +45 -22
- package/dist/embed.global.js.map +1 -1
- package/dist/index.cjs +45 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +45 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1276,6 +1276,12 @@ declare function hasEmailUserID(armoredKey: string): Promise<boolean>;
|
|
|
1276
1276
|
declare function fetchKeyByFingerprint(fingerprint: string): Promise<string | null>;
|
|
1277
1277
|
declare function fetchKeyByKeyId(keyId: string): Promise<string | null>;
|
|
1278
1278
|
|
|
1279
|
+
/**
|
|
1280
|
+
* EFP social graph for an address. Followers are counted by EFP for any address,
|
|
1281
|
+
* whether or not it has published a list of its own; following and top-8 exist
|
|
1282
|
+
* only when it has (its "primary list"). An address with followers but no list
|
|
1283
|
+
* — a project account that has never followed anyone — is still on EFP.
|
|
1284
|
+
*/
|
|
1279
1285
|
declare function fetchEFPGraph(address: string): Promise<EFPGraph | null>;
|
|
1280
1286
|
|
|
1281
1287
|
/** Normalize a fingerprint to lowercase hex with no prefix or spaces. Returns null if malformed. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1276,6 +1276,12 @@ declare function hasEmailUserID(armoredKey: string): Promise<boolean>;
|
|
|
1276
1276
|
declare function fetchKeyByFingerprint(fingerprint: string): Promise<string | null>;
|
|
1277
1277
|
declare function fetchKeyByKeyId(keyId: string): Promise<string | null>;
|
|
1278
1278
|
|
|
1279
|
+
/**
|
|
1280
|
+
* EFP social graph for an address. Followers are counted by EFP for any address,
|
|
1281
|
+
* whether or not it has published a list of its own; following and top-8 exist
|
|
1282
|
+
* only when it has (its "primary list"). An address with followers but no list
|
|
1283
|
+
* — a project account that has never followed anyone — is still on EFP.
|
|
1284
|
+
*/
|
|
1279
1285
|
declare function fetchEFPGraph(address: string): Promise<EFPGraph | null>;
|
|
1280
1286
|
|
|
1281
1287
|
/** Normalize a fingerprint to lowercase hex with no prefix or spaces. Returns null if malformed. */
|
package/dist/index.js
CHANGED
|
@@ -1339,7 +1339,9 @@ function keyIdToBytes(keyId) {
|
|
|
1339
1339
|
}
|
|
1340
1340
|
|
|
1341
1341
|
// src/core/pgp.ts
|
|
1342
|
-
|
|
1342
|
+
function pgpConfig(openpgp) {
|
|
1343
|
+
return { ...openpgp.config, rejectCurves: /* @__PURE__ */ new Set() };
|
|
1344
|
+
}
|
|
1343
1345
|
function algorithmName(keyOrSubkey) {
|
|
1344
1346
|
try {
|
|
1345
1347
|
const info = keyOrSubkey.getAlgorithmInfo();
|
|
@@ -1361,7 +1363,9 @@ function algorithmName(keyOrSubkey) {
|
|
|
1361
1363
|
}
|
|
1362
1364
|
async function parsePgpKey(armoredKey) {
|
|
1363
1365
|
try {
|
|
1364
|
-
const
|
|
1366
|
+
const openpgp = await import("openpgp");
|
|
1367
|
+
const { readKey } = openpgp;
|
|
1368
|
+
const config = pgpConfig(openpgp);
|
|
1365
1369
|
const key = await readKey({ armoredKey });
|
|
1366
1370
|
const fingerprint = key.getFingerprint().toUpperCase();
|
|
1367
1371
|
const algorithm = algorithmName(key);
|
|
@@ -1382,7 +1386,10 @@ async function parsePgpKey(armoredKey) {
|
|
|
1382
1386
|
cert.signatureType,
|
|
1383
1387
|
{ userID: user.userID, key: key.keyPacket },
|
|
1384
1388
|
void 0,
|
|
1385
|
-
|
|
1389
|
+
// date: now
|
|
1390
|
+
void 0,
|
|
1391
|
+
// detached
|
|
1392
|
+
config
|
|
1386
1393
|
);
|
|
1387
1394
|
} catch {
|
|
1388
1395
|
continue;
|
|
@@ -1426,15 +1433,28 @@ async function verifyAttestation({
|
|
|
1426
1433
|
if (!pgpPublicKey || !pgpSignature) {
|
|
1427
1434
|
return { verified: false, reason: "Missing PGP data" };
|
|
1428
1435
|
}
|
|
1429
|
-
const
|
|
1436
|
+
const openpgp = await import("openpgp");
|
|
1437
|
+
const { readKey, readCleartextMessage, LiteralDataPacket } = openpgp;
|
|
1438
|
+
const config = pgpConfig(openpgp);
|
|
1430
1439
|
const publicKey = await readKey({ armoredKey: pgpPublicKey });
|
|
1431
1440
|
const keyFingerprint = publicKey.getFingerprint().toUpperCase();
|
|
1432
1441
|
if (keyFingerprint !== fingerprint.toUpperCase()) {
|
|
1433
1442
|
return { verified: false, reason: "Key fingerprint mismatch" };
|
|
1434
1443
|
}
|
|
1435
1444
|
const message = await readCleartextMessage({ cleartextMessage: pgpSignature });
|
|
1436
|
-
const
|
|
1437
|
-
|
|
1445
|
+
const msg = message;
|
|
1446
|
+
const now = /* @__PURE__ */ new Date();
|
|
1447
|
+
let signed = false;
|
|
1448
|
+
for (const sig of msg.signature.packets) {
|
|
1449
|
+
if (!publicKey.getKeys(sig.issuerKeyID).length) continue;
|
|
1450
|
+
const signingKey = await publicKey.getSigningKey(sig.issuerKeyID, now, void 0, config);
|
|
1451
|
+
const literal = new LiteralDataPacket();
|
|
1452
|
+
literal.setText(msg.text ?? message.getText().replace(/\r?\n/g, "\r\n"));
|
|
1453
|
+
await sig.verify(signingKey.keyPacket, sig.signatureType, literal, now, true, config);
|
|
1454
|
+
signed = true;
|
|
1455
|
+
break;
|
|
1456
|
+
}
|
|
1457
|
+
if (!signed) return { verified: false, reason: "Message is not signed by this key" };
|
|
1438
1458
|
const signedText = message.getText();
|
|
1439
1459
|
if (!signedText.toLowerCase().includes(ethAddress.toLowerCase())) {
|
|
1440
1460
|
return { verified: false, reason: "Signed message does not contain ETH address" };
|
|
@@ -1579,25 +1599,28 @@ import { useQuery as useQuery2 } from "@tanstack/react-query";
|
|
|
1579
1599
|
var EFP_API = "https://api.ethfollow.xyz/api/v1";
|
|
1580
1600
|
async function fetchEFPGraph(address) {
|
|
1581
1601
|
try {
|
|
1582
|
-
const listsResp = await
|
|
1583
|
-
if (!listsResp.ok) return null;
|
|
1584
|
-
const listsData = await listsResp.json();
|
|
1585
|
-
const listId = listsData.primary_list;
|
|
1586
|
-
if (!listId) return null;
|
|
1587
|
-
const [followingResp, statsResp, top8Resp] = await Promise.all([
|
|
1588
|
-
fetch(`${EFP_API}/lists/${listId}/following?limit=100`),
|
|
1602
|
+
const [statsResp, listsResp] = await Promise.all([
|
|
1589
1603
|
fetch(`${EFP_API}/users/${address}/stats`),
|
|
1590
|
-
fetch(`${EFP_API}/
|
|
1604
|
+
fetch(`${EFP_API}/users/${address}/lists`)
|
|
1591
1605
|
]);
|
|
1592
|
-
|
|
1606
|
+
if (!statsResp.ok && !listsResp.ok) return null;
|
|
1593
1607
|
const statsData = statsResp.ok ? await statsResp.json() : {};
|
|
1594
|
-
const
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1608
|
+
const listsData = listsResp.ok ? await listsResp.json() : {};
|
|
1609
|
+
const followers = parseInt(statsData.followers_count) || 0;
|
|
1610
|
+
const listId = listsData.primary_list;
|
|
1611
|
+
let following = 0;
|
|
1612
|
+
let top8 = [];
|
|
1613
|
+
if (listId) {
|
|
1614
|
+
const [followingResp, top8Resp] = await Promise.all([
|
|
1615
|
+
fetch(`${EFP_API}/lists/${listId}/following?limit=100`),
|
|
1616
|
+
fetch(`${EFP_API}/lists/${listId}/following?limit=8&tags=top8`)
|
|
1617
|
+
]);
|
|
1618
|
+
const followingData = followingResp.ok ? await followingResp.json() : { following: [] };
|
|
1619
|
+
const top8Data = top8Resp.ok ? await top8Resp.json() : { following: [] };
|
|
1620
|
+
following = (followingData.following || []).length;
|
|
1621
|
+
top8 = (top8Data.following || []).map((f) => f.data);
|
|
1622
|
+
}
|
|
1623
|
+
return { followers, following, top8, hasEfp: !!listId || followers > 0 };
|
|
1601
1624
|
} catch {
|
|
1602
1625
|
return null;
|
|
1603
1626
|
}
|