@thurinlabs/identity-kit 1.3.6 → 1.4.0
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 +36 -9
- package/dist/{chunk-I3Y63ZPK.cjs → chunk-M2OJDB7I.cjs} +205 -48
- package/dist/chunk-M2OJDB7I.cjs.map +1 -0
- package/dist/{chunk-HDBHNN4I.js → chunk-ZAQ2EPF5.js} +185 -28
- package/dist/chunk-ZAQ2EPF5.js.map +1 -0
- package/dist/core.cjs +18 -2
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +85 -2
- package/dist/core.d.ts +85 -2
- package/dist/core.js +17 -1
- package/dist/embed.global.js +92 -30
- package/dist/embed.global.js.map +1 -1
- package/dist/index.cjs +53 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +21 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-HDBHNN4I.js.map +0 -1
- package/dist/chunk-I3Y63ZPK.cjs.map +0 -1
package/dist/core.d.ts
CHANGED
|
@@ -19,9 +19,24 @@ interface Attestation {
|
|
|
19
19
|
pgpPublicKey: string | null;
|
|
20
20
|
verification: PGPVerification | null;
|
|
21
21
|
}
|
|
22
|
+
/** Why a claim does or doesn't count, in a form a UI can word (see CLAIM_CHECK_LABEL). Since 1.4.0. */
|
|
23
|
+
type ClaimCheckKind = 'verified' | 'expired' | 'signing-key-expired' | 'revoked' | 'compromised' | 'signing-key-revoked' | 'unsupported' | 'bad-signature';
|
|
22
24
|
interface PGPVerification {
|
|
23
25
|
verified: boolean;
|
|
26
|
+
/** The underlying library message, for logs; show `kind` to people instead. */
|
|
24
27
|
reason?: string;
|
|
28
|
+
/** Since 1.4.0. */
|
|
29
|
+
kind?: ClaimCheckKind;
|
|
30
|
+
/** ISO date that goes with `kind`: when the key or subkey expired or was revoked. */
|
|
31
|
+
at?: string | null;
|
|
32
|
+
/** The owner's revocation reason (their words, or the reason code's name). */
|
|
33
|
+
revocationReason?: string | null;
|
|
34
|
+
/** Fingerprint of the key or subkey that made the claim's signature. */
|
|
35
|
+
signingKey?: string | null;
|
|
36
|
+
/** When verified: the earlier expiry of the key and the signing subkey (ISO), or null if neither expires. */
|
|
37
|
+
expiresAt?: string | null;
|
|
38
|
+
/** For `unsupported`: the algorithm, e.g. "DSA 2048". */
|
|
39
|
+
algorithm?: string | null;
|
|
25
40
|
}
|
|
26
41
|
interface PGPKeyInfo {
|
|
27
42
|
fingerprint: string;
|
|
@@ -90,7 +105,10 @@ interface ThurinIdentity {
|
|
|
90
105
|
type Theme = 'thurin' | 'dark' | 'light';
|
|
91
106
|
interface IdentityKitConfig {
|
|
92
107
|
rpcUrl?: string;
|
|
108
|
+
/** Optional: read Farcaster proofs through Neynar's hub with this key. Not needed since 1.3.7. */
|
|
93
109
|
neynarApiKey?: string;
|
|
110
|
+
/** A Farcaster node's HTTP API for Farcaster proofs. Default: Quilibrium's public Hypersnap node (keyless). */
|
|
111
|
+
farcasterHub?: string;
|
|
94
112
|
baseUrl?: string;
|
|
95
113
|
/** Which PGPRegistry deployment to read: 'mainnet' (default) or 'sepolia'. */
|
|
96
114
|
network?: 'mainnet' | 'sepolia' | 'local';
|
|
@@ -1715,7 +1733,24 @@ declare function identifyProof(notation: Notation): Proof | null;
|
|
|
1715
1733
|
declare function displayUrl(proof: Proof): string;
|
|
1716
1734
|
declare function proofHref(proof: Proof): string | null;
|
|
1717
1735
|
declare function proofSecondaryHref(proof: Proof): string | null;
|
|
1718
|
-
|
|
1736
|
+
/**
|
|
1737
|
+
* Farcaster proofs are read from a Farcaster node's HTTP API (`/v1/userNameProofByName`,
|
|
1738
|
+
* `/v1/castsByFid`). The default is Quilibrium's public Hypersnap node, which needs no key and
|
|
1739
|
+
* allows browser requests; it sees the visitor's IP and which account was checked. Point
|
|
1740
|
+
* `farcasterHub` at your own node to avoid that, or pass a Neynar key to use Neynar's hub.
|
|
1741
|
+
*/
|
|
1742
|
+
declare const FARCASTER_HUB = "https://haatz.quilibrium.com";
|
|
1743
|
+
interface ProofOptions {
|
|
1744
|
+
/** A Farcaster node's HTTP API base URL. Default: FARCASTER_HUB (keyless). */
|
|
1745
|
+
farcasterHub?: string;
|
|
1746
|
+
/** Optional: read Farcaster through Neynar's hub with this key instead. */
|
|
1747
|
+
neynarApiKey?: string;
|
|
1748
|
+
}
|
|
1749
|
+
/**
|
|
1750
|
+
* Check one proof. The third argument is options; a plain string is still accepted as a
|
|
1751
|
+
* Neynar API key (the pre-1.3.7 signature).
|
|
1752
|
+
*/
|
|
1753
|
+
declare function verifyProof(proof: Proof, fingerprint: string, opts?: ProofOptions | string): Promise<PGPVerification>;
|
|
1719
1754
|
|
|
1720
1755
|
declare function parsePgpKey(armoredKey: string): Promise<PGPKeyInfo | null>;
|
|
1721
1756
|
/**
|
|
@@ -1802,6 +1837,54 @@ declare function needsRpcProbe(s: Omit<IdentityLookupState, 'rpcAnswered'>): boo
|
|
|
1802
1837
|
declare function identityErrorKind(s: IdentityLookupState): IdentityErrorKind | null;
|
|
1803
1838
|
declare const IDENTITY_ERROR_TEXT: Record<IdentityErrorKind, string>;
|
|
1804
1839
|
|
|
1840
|
+
/**
|
|
1841
|
+
* Plain words for a claim's state, shared by thurin.id, the CLI, and anyone embedding the kit,
|
|
1842
|
+
* so every surface says the same thing. `sentence` is for visitors; `fix` is for the owner only
|
|
1843
|
+
* (thurin.id shows it when the connected wallet owns the claim).
|
|
1844
|
+
*/
|
|
1845
|
+
/** Badge text for each check result. */
|
|
1846
|
+
declare const CLAIM_CHECK_LABEL: Record<ClaimCheckKind, string>;
|
|
1847
|
+
/** "Mar 5, 2029", in UTC so every viewer sees the same day. */
|
|
1848
|
+
declare function formatClaimDate(isoOrSeconds: string | number | null | undefined): string;
|
|
1849
|
+
interface ClaimCheckText {
|
|
1850
|
+
kind: ClaimCheckKind;
|
|
1851
|
+
label: string;
|
|
1852
|
+
sentence: string;
|
|
1853
|
+
/** What the owner can do about it, when there is something. */
|
|
1854
|
+
fix: string | null;
|
|
1855
|
+
}
|
|
1856
|
+
/** Label, visitor sentence, and owner fix for a verification result. */
|
|
1857
|
+
declare function claimCheckText(v: PGPVerification, formatDate?: (iso: string | null | undefined) => string): ClaimCheckText;
|
|
1858
|
+
/** Days until a verified claim's key (or signing subkey) expires, when that is within `withinDays`. */
|
|
1859
|
+
declare function expiresSoon(v: PGPVerification | null | undefined, now?: number, withinDays?: number): {
|
|
1860
|
+
days: number;
|
|
1861
|
+
at: string;
|
|
1862
|
+
} | null;
|
|
1863
|
+
/** "Key expires in 12 days (Mar 6, 2027)." */
|
|
1864
|
+
declare function expiresSoonText(soon: {
|
|
1865
|
+
days: number;
|
|
1866
|
+
at: string;
|
|
1867
|
+
}, formatDate?: (iso: string) => string): string;
|
|
1868
|
+
type ClaimFate = {
|
|
1869
|
+
state: 'active';
|
|
1870
|
+
} | {
|
|
1871
|
+
state: 'revoked';
|
|
1872
|
+
at: number;
|
|
1873
|
+
} | {
|
|
1874
|
+
state: 'replaced';
|
|
1875
|
+
at: number;
|
|
1876
|
+
by: number;
|
|
1877
|
+
};
|
|
1878
|
+
/**
|
|
1879
|
+
* Active, revoked, or replaced, for each of one owner's claims (keyed by index). `reattest`
|
|
1880
|
+
* revokes and attests in one transaction, so a claim revoked at the very second a newer claim
|
|
1881
|
+
* of the same owner was created counts as replaced by it. The contract has no "superseded"
|
|
1882
|
+
* event; this reads the same fact from the stored timestamps.
|
|
1883
|
+
*/
|
|
1884
|
+
declare function claimFates(claims: Pick<Attestation, 'index' | 'createdAt' | 'revokedAt'>[]): Map<number, ClaimFate>;
|
|
1885
|
+
/** "Revoked by its owner on Oct 3, 2026." / "Replaced by claim #2 on Oct 3, 2026." */
|
|
1886
|
+
declare function claimFateText(fate: ClaimFate, formatDate?: (seconds: number) => string): string | null;
|
|
1887
|
+
|
|
1805
1888
|
/**
|
|
1806
1889
|
* ENS avatars without the tracking pixel. The `avatar` record is a link the name's owner
|
|
1807
1890
|
* chooses; loading an ordinary https:// link would tell that owner's server the IP and time of
|
|
@@ -1875,4 +1958,4 @@ declare function keyIdOf(fingerprint: string): Hex;
|
|
|
1875
1958
|
/** A 16-hex long key ID (spaces/0x tolerated) → `bytes8`. */
|
|
1876
1959
|
declare function keyIdToBytes(keyId: string): Hex | null;
|
|
1877
1960
|
|
|
1878
|
-
export { ARWEAVE_GATEWAY, AUTHORIZATION_TYPES, type AttestAuthorization, type Attestation, type AuthorizationAction, type EFPGraph, EIP712_NAME, EIP712_VERSION, ENS_HINT_KEY, ENS_TEXT_RESOLVER_ABI, type EnsHint, type EnsHintState, type EnsTextReader, IDENTITY_ERROR_TEXT, IDENTITY_KINDS, IPFS_GATEWAY, IPFS_GATEWAYS, type IdentityErrorKind, type IdentityKitConfig, type IdentityLookupState, KNOWN_KINDS, type KnownKind, MAX_RECORD_BYTES, NETWORKS, NFT_AVATAR_ABI, type NetworkName, type NftAvatar, type Notation, type PGPKeyInfo, type PGPVerification, type ParsedRecord, type PointerEntry, type PointerRecord, type Proof, type ProofResult, REGISTRY_ABI, REGISTRY_ADDRESS, type ReattestAuthorization, type RecordData, type RecordReader, type RegistryDeployment, type RevokeAuthorization, type SetRecordAuthorization, type StrippedKey, type SubkeyInfo, type Theme, type ThurinIdentity, type UpdateKeyAuthorization, addPointer, attestTypedData, authorizationDigest, avatarFallbacks, avatarUrl, bytesToFingerprint, chainFor, decodeRecord, displayUrl, encodeRecord, ensHintFor, ensHintValue, ensHintWrite, fetchEFPGraph, fetchEnsHint, fetchRecords, fingerprintHash, fingerprintToBytes, getRegistry, hasEmailUserID, identifyProof, identityErrorKind, isNetworkName, keyIdOf, keyIdToBytes, kindName, needsRpcProbe, nftAvatarImage, normalizeFingerprint, parseNftAvatar, parsePgpKey, parsePointer, parseRecord, proofHref, proofSecondaryHref, reattestTypedData, recordKind, registryDomain, renderPointer, revokeTypedData, setRecordTypedData, stripEmailUserIDs, updateKeyTypedData, verifyAttestation, verifyClearsigned, verifyProof };
|
|
1961
|
+
export { ARWEAVE_GATEWAY, AUTHORIZATION_TYPES, type AttestAuthorization, type Attestation, type AuthorizationAction, CLAIM_CHECK_LABEL, type ClaimCheckKind, type ClaimCheckText, type ClaimFate, type EFPGraph, EIP712_NAME, EIP712_VERSION, ENS_HINT_KEY, ENS_TEXT_RESOLVER_ABI, type EnsHint, type EnsHintState, type EnsTextReader, FARCASTER_HUB, IDENTITY_ERROR_TEXT, IDENTITY_KINDS, IPFS_GATEWAY, IPFS_GATEWAYS, type IdentityErrorKind, type IdentityKitConfig, type IdentityLookupState, KNOWN_KINDS, type KnownKind, MAX_RECORD_BYTES, NETWORKS, NFT_AVATAR_ABI, type NetworkName, type NftAvatar, type Notation, type PGPKeyInfo, type PGPVerification, type ParsedRecord, type PointerEntry, type PointerRecord, type Proof, type ProofOptions, type ProofResult, REGISTRY_ABI, REGISTRY_ADDRESS, type ReattestAuthorization, type RecordData, type RecordReader, type RegistryDeployment, type RevokeAuthorization, type SetRecordAuthorization, type StrippedKey, type SubkeyInfo, type Theme, type ThurinIdentity, type UpdateKeyAuthorization, addPointer, attestTypedData, authorizationDigest, avatarFallbacks, avatarUrl, bytesToFingerprint, chainFor, claimCheckText, claimFateText, claimFates, decodeRecord, displayUrl, encodeRecord, ensHintFor, ensHintValue, ensHintWrite, expiresSoon, expiresSoonText, fetchEFPGraph, fetchEnsHint, fetchRecords, fingerprintHash, fingerprintToBytes, formatClaimDate, getRegistry, hasEmailUserID, identifyProof, identityErrorKind, isNetworkName, keyIdOf, keyIdToBytes, kindName, needsRpcProbe, nftAvatarImage, normalizeFingerprint, parseNftAvatar, parsePgpKey, parsePointer, parseRecord, proofHref, proofSecondaryHref, reattestTypedData, recordKind, registryDomain, renderPointer, revokeTypedData, setRecordTypedData, stripEmailUserIDs, updateKeyTypedData, verifyAttestation, verifyClearsigned, verifyProof };
|
package/dist/core.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ARWEAVE_GATEWAY,
|
|
3
3
|
AUTHORIZATION_TYPES,
|
|
4
|
+
CLAIM_CHECK_LABEL,
|
|
4
5
|
EIP712_NAME,
|
|
5
6
|
EIP712_VERSION,
|
|
6
7
|
ENS_HINT_KEY,
|
|
7
8
|
ENS_TEXT_RESOLVER_ABI,
|
|
9
|
+
FARCASTER_HUB,
|
|
8
10
|
IDENTITY_ERROR_TEXT,
|
|
9
11
|
IDENTITY_KINDS,
|
|
10
12
|
IPFS_GATEWAY,
|
|
@@ -22,17 +24,23 @@ import {
|
|
|
22
24
|
avatarUrl,
|
|
23
25
|
bytesToFingerprint,
|
|
24
26
|
chainFor,
|
|
27
|
+
claimCheckText,
|
|
28
|
+
claimFateText,
|
|
29
|
+
claimFates,
|
|
25
30
|
decodeRecord,
|
|
26
31
|
displayUrl,
|
|
27
32
|
encodeRecord,
|
|
28
33
|
ensHintFor,
|
|
29
34
|
ensHintValue,
|
|
30
35
|
ensHintWrite,
|
|
36
|
+
expiresSoon,
|
|
37
|
+
expiresSoonText,
|
|
31
38
|
fetchEFPGraph,
|
|
32
39
|
fetchEnsHint,
|
|
33
40
|
fetchRecords,
|
|
34
41
|
fingerprintHash,
|
|
35
42
|
fingerprintToBytes,
|
|
43
|
+
formatClaimDate,
|
|
36
44
|
getRegistry,
|
|
37
45
|
hasEmailUserID,
|
|
38
46
|
identifyProof,
|
|
@@ -61,14 +69,16 @@ import {
|
|
|
61
69
|
verifyAttestation,
|
|
62
70
|
verifyClearsigned,
|
|
63
71
|
verifyProof
|
|
64
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-ZAQ2EPF5.js";
|
|
65
73
|
export {
|
|
66
74
|
ARWEAVE_GATEWAY,
|
|
67
75
|
AUTHORIZATION_TYPES,
|
|
76
|
+
CLAIM_CHECK_LABEL,
|
|
68
77
|
EIP712_NAME,
|
|
69
78
|
EIP712_VERSION,
|
|
70
79
|
ENS_HINT_KEY,
|
|
71
80
|
ENS_TEXT_RESOLVER_ABI,
|
|
81
|
+
FARCASTER_HUB,
|
|
72
82
|
IDENTITY_ERROR_TEXT,
|
|
73
83
|
IDENTITY_KINDS,
|
|
74
84
|
IPFS_GATEWAY,
|
|
@@ -86,17 +96,23 @@ export {
|
|
|
86
96
|
avatarUrl,
|
|
87
97
|
bytesToFingerprint,
|
|
88
98
|
chainFor,
|
|
99
|
+
claimCheckText,
|
|
100
|
+
claimFateText,
|
|
101
|
+
claimFates,
|
|
89
102
|
decodeRecord,
|
|
90
103
|
displayUrl,
|
|
91
104
|
encodeRecord,
|
|
92
105
|
ensHintFor,
|
|
93
106
|
ensHintValue,
|
|
94
107
|
ensHintWrite,
|
|
108
|
+
expiresSoon,
|
|
109
|
+
expiresSoonText,
|
|
95
110
|
fetchEFPGraph,
|
|
96
111
|
fetchEnsHint,
|
|
97
112
|
fetchRecords,
|
|
98
113
|
fingerprintHash,
|
|
99
114
|
fingerprintToBytes,
|
|
115
|
+
formatClaimDate,
|
|
100
116
|
getRegistry,
|
|
101
117
|
hasEmailUserID,
|
|
102
118
|
identifyProof,
|
package/dist/embed.global.js
CHANGED
|
@@ -30400,13 +30400,14 @@ ${prettyStateOverride(stateOverride)}`;
|
|
|
30400
30400
|
children,
|
|
30401
30401
|
rpcUrl,
|
|
30402
30402
|
neynarApiKey,
|
|
30403
|
+
farcasterHub,
|
|
30403
30404
|
baseUrl = "https://thurin.id",
|
|
30404
30405
|
network = "mainnet",
|
|
30405
30406
|
registryAddress
|
|
30406
30407
|
}) {
|
|
30407
30408
|
const config = (0, import_react8.useMemo)(
|
|
30408
|
-
() => ({ rpcUrl, neynarApiKey, baseUrl, network, registryAddress }),
|
|
30409
|
-
[rpcUrl, neynarApiKey, baseUrl, network, registryAddress]
|
|
30409
|
+
() => ({ rpcUrl, neynarApiKey, farcasterHub, baseUrl, network, registryAddress }),
|
|
30410
|
+
[rpcUrl, neynarApiKey, farcasterHub, baseUrl, network, registryAddress]
|
|
30410
30411
|
);
|
|
30411
30412
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IdentityKitContext.Provider, { value: config, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(WagmiDetector, { rpcUrl, network, children }) });
|
|
30412
30413
|
}
|
|
@@ -31341,6 +31342,61 @@ ${prettyStateOverride(stateOverride)}`;
|
|
|
31341
31342
|
return { verified: false, reason: err instanceof Error ? err.message : "Verification failed", text: "" };
|
|
31342
31343
|
}
|
|
31343
31344
|
}
|
|
31345
|
+
var REVOCATION_REASON = {
|
|
31346
|
+
// 0 = "no reason specified", gpg's default: say nothing rather than "(reason: no reason given)"
|
|
31347
|
+
1: "replaced by a newer key",
|
|
31348
|
+
2: "compromised",
|
|
31349
|
+
3: "no longer used",
|
|
31350
|
+
32: "a user ID is no longer valid"
|
|
31351
|
+
};
|
|
31352
|
+
var iso = (d2) => d2 instanceof Date ? d2.toISOString() : typeof d2 === "number" && Number.isFinite(d2) ? new Date(d2).toISOString() : null;
|
|
31353
|
+
function revocationOf(sigs) {
|
|
31354
|
+
const sig = [...sigs ?? []].sort((a2, b2) => (b2.created?.getTime?.() ?? 0) - (a2.created?.getTime?.() ?? 0))[0];
|
|
31355
|
+
const flag = sig?.reasonForRevocationFlag ?? null;
|
|
31356
|
+
return {
|
|
31357
|
+
at: iso(sig?.created),
|
|
31358
|
+
compromised: flag === 2,
|
|
31359
|
+
revocationReason: sig?.reasonForRevocationString || (flag !== null ? REVOCATION_REASON[flag] ?? null : null)
|
|
31360
|
+
};
|
|
31361
|
+
}
|
|
31362
|
+
async function keyLife(openpgp, key, clearsigned, config) {
|
|
31363
|
+
const now = /* @__PURE__ */ new Date();
|
|
31364
|
+
let issuer = null;
|
|
31365
|
+
try {
|
|
31366
|
+
issuer = (await openpgp.readCleartextMessage({ cleartextMessage: clearsigned })).signature.packets[0]?.issuerKeyID ?? null;
|
|
31367
|
+
} catch {
|
|
31368
|
+
}
|
|
31369
|
+
const signer = issuer && !key.getKeyID().equals(issuer) ? key.subkeys.find((s2) => s2.getKeyID().equals(issuer)) ?? null : key;
|
|
31370
|
+
const signingKey = signer ? signer.getFingerprint().toUpperCase() : null;
|
|
31371
|
+
const safe = async (f2, fallback2) => {
|
|
31372
|
+
try {
|
|
31373
|
+
return await f2();
|
|
31374
|
+
} catch {
|
|
31375
|
+
return fallback2;
|
|
31376
|
+
}
|
|
31377
|
+
};
|
|
31378
|
+
const keyRevoked = await safe(() => key.isRevoked(void 0, void 0, now, config), false);
|
|
31379
|
+
const keyExp = await safe(() => key.getExpirationTime(void 0, config), null);
|
|
31380
|
+
const isSub = signer && signer !== key;
|
|
31381
|
+
const subRevoked = isSub ? await safe(() => signer.isRevoked(null, key.keyPacket, now, config), false) : false;
|
|
31382
|
+
const subExp = isSub ? await safe(() => signer.getExpirationTime(null, config), null) : null;
|
|
31383
|
+
const finite = (d2) => d2 instanceof Date && Number.isFinite(d2.getTime()) ? d2 : null;
|
|
31384
|
+
const kExp = finite(keyExp), sExp = finite(subExp);
|
|
31385
|
+
let problem = null;
|
|
31386
|
+
if (keyRevoked) {
|
|
31387
|
+
const r2 = revocationOf(key.revocationSignatures);
|
|
31388
|
+
problem = { kind: r2.compromised ? "compromised" : "revoked", at: r2.at, revocationReason: r2.revocationReason };
|
|
31389
|
+
} else if (kExp && kExp <= now) {
|
|
31390
|
+
problem = { kind: "expired", at: kExp.toISOString() };
|
|
31391
|
+
} else if (subRevoked) {
|
|
31392
|
+
const r2 = revocationOf(signer.revocationSignatures);
|
|
31393
|
+
problem = { kind: r2.compromised ? "compromised" : "signing-key-revoked", at: r2.at, revocationReason: r2.revocationReason };
|
|
31394
|
+
} else if (sExp && sExp <= now) {
|
|
31395
|
+
problem = { kind: "signing-key-expired", at: sExp.toISOString() };
|
|
31396
|
+
}
|
|
31397
|
+
const earliest = [kExp, sExp].filter(Boolean).sort((a2, b2) => a2 - b2)[0];
|
|
31398
|
+
return { problem, signingKey, expiresAt: earliest ? earliest.toISOString() : null, algorithm: algorithmName(signer ?? key) };
|
|
31399
|
+
}
|
|
31344
31400
|
async function verifyAttestation({
|
|
31345
31401
|
pgpPublicKey,
|
|
31346
31402
|
pgpSignature,
|
|
@@ -31349,24 +31405,31 @@ ${prettyStateOverride(stateOverride)}`;
|
|
|
31349
31405
|
}) {
|
|
31350
31406
|
try {
|
|
31351
31407
|
if (!pgpPublicKey || !pgpSignature) {
|
|
31352
|
-
return { verified: false, reason: "Missing PGP data" };
|
|
31408
|
+
return { verified: false, reason: "Missing PGP data", kind: "bad-signature" };
|
|
31353
31409
|
}
|
|
31354
31410
|
const openpgp = await Promise.resolve().then(() => (init_openpgp_min(), openpgp_min_exports));
|
|
31355
31411
|
const { readKey } = openpgp;
|
|
31356
31412
|
const publicKey = await readKey({ armoredKey: pgpPublicKey });
|
|
31357
31413
|
const keyFingerprint = publicKey.getFingerprint().toUpperCase();
|
|
31358
31414
|
if (keyFingerprint !== fingerprint.toUpperCase()) {
|
|
31359
|
-
return { verified: false, reason: "Key fingerprint mismatch" };
|
|
31415
|
+
return { verified: false, reason: "Key fingerprint mismatch", kind: "bad-signature" };
|
|
31360
31416
|
}
|
|
31417
|
+
const life = await keyLife(openpgp, publicKey, pgpSignature, pgpConfig(openpgp));
|
|
31361
31418
|
const v2 = await verifyClearsigned({ armoredKey: pgpPublicKey, clearsigned: pgpSignature });
|
|
31362
|
-
if (!v2.verified)
|
|
31419
|
+
if (!v2.verified) {
|
|
31420
|
+
if (life.problem) return { verified: false, reason: v2.reason, signingKey: life.signingKey, ...life.problem };
|
|
31421
|
+
if (/too weak|reject|unsupported|not supported|unknown (curve|algorithm)/i.test(v2.reason || "")) {
|
|
31422
|
+
return { verified: false, reason: v2.reason, kind: "unsupported", algorithm: life.algorithm, signingKey: life.signingKey };
|
|
31423
|
+
}
|
|
31424
|
+
return { verified: false, reason: v2.reason, kind: "bad-signature", signingKey: life.signingKey };
|
|
31425
|
+
}
|
|
31363
31426
|
const signedText = v2.text;
|
|
31364
31427
|
if (!signedText.toLowerCase().includes(ethAddress.toLowerCase())) {
|
|
31365
|
-
return { verified: false, reason: "Signed message does not contain ETH address" };
|
|
31428
|
+
return { verified: false, reason: "Signed message does not contain ETH address", kind: "bad-signature", signingKey: life.signingKey };
|
|
31366
31429
|
}
|
|
31367
|
-
return { verified: true };
|
|
31430
|
+
return { verified: true, kind: "verified", signingKey: life.signingKey, expiresAt: life.expiresAt };
|
|
31368
31431
|
} catch {
|
|
31369
|
-
return { verified: false, reason: "Signature verification failed" };
|
|
31432
|
+
return { verified: false, reason: "Signature verification failed", kind: "bad-signature" };
|
|
31370
31433
|
}
|
|
31371
31434
|
}
|
|
31372
31435
|
|
|
@@ -31640,29 +31703,27 @@ ${prettyStateOverride(stateOverride)}`;
|
|
|
31640
31703
|
return { verified: false, reason: `DNS fetch failed: ${err.message}` };
|
|
31641
31704
|
}
|
|
31642
31705
|
}
|
|
31706
|
+
var FARCASTER_HUB = "https://haatz.quilibrium.com";
|
|
31643
31707
|
var NEYNAR_HUB = "https://hub-api.neynar.com";
|
|
31644
|
-
|
|
31645
|
-
|
|
31646
|
-
|
|
31647
|
-
|
|
31648
|
-
|
|
31649
|
-
|
|
31650
|
-
const
|
|
31651
|
-
|
|
31652
|
-
}
|
|
31653
|
-
async function verifyFarcaster(proof, fingerprint, neynarApiKey) {
|
|
31654
|
-
if (!neynarApiKey) {
|
|
31655
|
-
return { verified: false, reason: "Farcaster verification requires a Neynar API key" };
|
|
31656
|
-
}
|
|
31708
|
+
function farcasterSource(opts) {
|
|
31709
|
+
if (opts.farcasterHub) return { base: opts.farcasterHub.replace(/\/+$/, ""), headers: {} };
|
|
31710
|
+
if (opts.neynarApiKey) return { base: NEYNAR_HUB, headers: { "x-api-key": opts.neynarApiKey } };
|
|
31711
|
+
return { base: FARCASTER_HUB, headers: {} };
|
|
31712
|
+
}
|
|
31713
|
+
async function verifyFarcaster(proof, fingerprint, opts = {}) {
|
|
31714
|
+
const { base, headers } = farcasterSource(opts);
|
|
31715
|
+
const host = base.replace(/^https?:\/\//, "");
|
|
31657
31716
|
try {
|
|
31658
|
-
const
|
|
31717
|
+
const nameResp = await fetch(`${base}/v1/userNameProofByName?name=${encodeURIComponent(proof.user)}`, { headers });
|
|
31718
|
+
if (nameResp.status === 404) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
|
|
31719
|
+
if (!nameResp.ok) return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) returned ${nameResp.status}` };
|
|
31720
|
+
const fid = (await nameResp.json()).fid;
|
|
31659
31721
|
if (!fid) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
|
|
31660
|
-
const headers = { "x-api-key": neynarApiKey };
|
|
31661
31722
|
let pageToken = "";
|
|
31662
31723
|
for (let page = 0; page < 5; page++) {
|
|
31663
|
-
const url = `${
|
|
31724
|
+
const url = `${base}/v1/castsByFid?fid=${fid}&pageSize=100&reverse=true${pageToken ? `&pageToken=${pageToken}` : ""}`;
|
|
31664
31725
|
const resp = await fetch(url, { headers });
|
|
31665
|
-
if (!resp.ok) return { verified: false, reason: `Farcaster
|
|
31726
|
+
if (!resp.ok) return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) returned ${resp.status}` };
|
|
31666
31727
|
const data = await resp.json();
|
|
31667
31728
|
for (const msg of data.messages || []) {
|
|
31668
31729
|
if (msg.hash && msg.hash.startsWith(proof.castHash)) {
|
|
@@ -31677,7 +31738,7 @@ ${prettyStateOverride(stateOverride)}`;
|
|
|
31677
31738
|
}
|
|
31678
31739
|
return { verified: false, reason: "Cast not found" };
|
|
31679
31740
|
} catch (err) {
|
|
31680
|
-
return { verified: false, reason: `Farcaster
|
|
31741
|
+
return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) didn't answer (${err.message})` };
|
|
31681
31742
|
}
|
|
31682
31743
|
}
|
|
31683
31744
|
async function verifyCodeberg(proof, fingerprint) {
|
|
@@ -31730,10 +31791,10 @@ ${prettyStateOverride(stateOverride)}`;
|
|
|
31730
31791
|
codeberg: verifyCodeberg,
|
|
31731
31792
|
mastodon: verifyMastodon
|
|
31732
31793
|
};
|
|
31733
|
-
async function verifyProof(proof, fingerprint,
|
|
31794
|
+
async function verifyProof(proof, fingerprint, opts) {
|
|
31734
31795
|
const fn2 = verifiers[proof.provider];
|
|
31735
31796
|
if (!fn2) return { verified: false, reason: "Unknown provider" };
|
|
31736
|
-
return fn2(proof, fingerprint, neynarApiKey);
|
|
31797
|
+
return fn2(proof, fingerprint, typeof opts === "string" ? { neynarApiKey: opts } : opts ?? {});
|
|
31737
31798
|
}
|
|
31738
31799
|
|
|
31739
31800
|
// src/hooks/usePGPProofs.ts
|
|
@@ -31750,7 +31811,7 @@ ${prettyStateOverride(stateOverride)}`;
|
|
|
31750
31811
|
const identified = keyInfo.notations.map((n2) => identifyProof(n2)).filter((p2) => p2 !== null && p2.provider !== "unknown");
|
|
31751
31812
|
const proofs = await Promise.all(
|
|
31752
31813
|
identified.map(async (proof) => {
|
|
31753
|
-
const result = await verifyProof(proof, fingerprint, config.neynarApiKey);
|
|
31814
|
+
const result = await verifyProof(proof, fingerprint, { neynarApiKey: config.neynarApiKey, farcasterHub: config.farcasterHub });
|
|
31754
31815
|
return {
|
|
31755
31816
|
provider: proof.provider,
|
|
31756
31817
|
label: proof.label,
|
|
@@ -32054,6 +32115,7 @@ ${prettyStateOverride(stateOverride)}`;
|
|
|
32054
32115
|
const theme = el2.dataset.theme || "thurin";
|
|
32055
32116
|
const rpcUrl = el2.dataset.rpcUrl;
|
|
32056
32117
|
const neynarApiKey = el2.dataset.neynarKey;
|
|
32118
|
+
const farcasterHub = el2.dataset.farcasterHub;
|
|
32057
32119
|
const baseUrl = el2.dataset.baseUrl;
|
|
32058
32120
|
const network = isNetworkName(el2.dataset.network) ? el2.dataset.network : "mainnet";
|
|
32059
32121
|
const registryAddress = el2.dataset.registryAddress;
|
|
@@ -32066,7 +32128,7 @@ ${prettyStateOverride(stateOverride)}`;
|
|
|
32066
32128
|
shadow.appendChild(container);
|
|
32067
32129
|
const root = (0, import_client.createRoot)(container);
|
|
32068
32130
|
const render = (t2) => root.render(
|
|
32069
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IdentityKitProvider, { rpcUrl, neynarApiKey, network, registryAddress, baseUrl, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ThurinCard, { ...props, theme: t2 }) })
|
|
32131
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IdentityKitProvider, { rpcUrl, neynarApiKey, farcasterHub, network, registryAddress, baseUrl, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ThurinCard, { ...props, theme: t2 }) })
|
|
32070
32132
|
);
|
|
32071
32133
|
render(theme);
|
|
32072
32134
|
new MutationObserver(() => {
|