@thru/passkey 0.2.22 → 0.2.24
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/dist/auth/add-device.cjs +50 -29
- package/dist/auth/add-device.cjs.map +1 -1
- package/dist/auth/add-device.js +1 -1
- package/dist/auth.cjs +50 -29
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.js +1 -1
- package/dist/{chunk-QAQNRQ7G.js → chunk-KASTJBBY.js} +55 -31
- package/dist/chunk-KASTJBBY.js.map +1 -0
- package/dist/server.cjs +24 -6
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +12 -4
- package/dist/server.d.ts +12 -4
- package/dist/server.js +26 -7
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
- package/src/auth/add-device.ts +60 -37
- package/src/server/challenge.ts +13 -2
- package/src/server/handlers.ts +6 -2
- package/src/server/submit.test.ts +23 -5
- package/src/server/submit.ts +16 -6
- package/src/server/types.ts +3 -1
- package/dist/chunk-QAQNRQ7G.js.map +0 -1
package/dist/auth/add-device.cjs
CHANGED
|
@@ -24,6 +24,7 @@ __export(add_device_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(add_device_exports);
|
|
26
26
|
var import_passkey_manager = require("@thru/programs/passkey-manager");
|
|
27
|
+
var import_multicall = require("@thru/programs/multicall");
|
|
27
28
|
async function addDeviceToAccount(params) {
|
|
28
29
|
const status = params.onStatus ?? (() => {
|
|
29
30
|
});
|
|
@@ -43,14 +44,13 @@ async function addDeviceToAccount(params) {
|
|
|
43
44
|
);
|
|
44
45
|
}
|
|
45
46
|
const newAuthorityIdx = parsed.authorities.length;
|
|
46
|
-
const addAuthorityInstruction = (0, import_passkey_manager.encodeAddAuthorityInstruction)({
|
|
47
|
-
authority: params.newAuthority
|
|
48
|
-
});
|
|
49
|
-
let trailingInstructionData = addAuthorityInstruction;
|
|
50
47
|
let readWriteAccounts = [];
|
|
48
|
+
let lookupSeed;
|
|
49
|
+
let lookupAddressBytes;
|
|
50
|
+
let lookupProof;
|
|
51
51
|
if (params.credentialId) {
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
lookupSeed = await (0, import_passkey_manager.createCredentialLookupSeed)(params.credentialId);
|
|
53
|
+
lookupAddressBytes = await (0, import_passkey_manager.deriveWalletAddress)(
|
|
54
54
|
lookupSeed,
|
|
55
55
|
params.programAddress
|
|
56
56
|
);
|
|
@@ -59,55 +59,76 @@ async function addDeviceToAccount(params) {
|
|
|
59
59
|
proofType: 1,
|
|
60
60
|
address: lookupAddressBytes
|
|
61
61
|
});
|
|
62
|
-
|
|
63
|
-
walletAddress: params.walletAddress,
|
|
64
|
-
readWriteAccounts: [lookupAddressBytes],
|
|
65
|
-
readOnlyAccounts: []
|
|
66
|
-
});
|
|
67
|
-
const registerCredentialInstruction = (0, import_passkey_manager.encodeRegisterCredentialInstruction)({
|
|
68
|
-
walletAccountIdx: ctx2.walletAccountIdx,
|
|
69
|
-
lookupAccountIdx: ctx2.getAccountIndex(lookupAddressBytes),
|
|
70
|
-
seed: lookupSeed,
|
|
71
|
-
stateProof: proofResult.proof
|
|
72
|
-
});
|
|
73
|
-
trailingInstructionData = (0, import_passkey_manager.concatenateInstructions)([
|
|
74
|
-
addAuthorityInstruction,
|
|
75
|
-
registerCredentialInstruction
|
|
76
|
-
]);
|
|
62
|
+
lookupProof = proofResult.proof;
|
|
77
63
|
readWriteAccounts = [lookupAddressBytes];
|
|
78
64
|
}
|
|
79
65
|
const ctx = (0, import_passkey_manager.buildAccountContext)({
|
|
80
66
|
walletAddress: params.walletAddress,
|
|
81
67
|
readWriteAccounts,
|
|
82
|
-
readOnlyAccounts: []
|
|
68
|
+
readOnlyAccounts: params.credentialId ? [import_multicall.MULTICALL_PROGRAM_PUBKEY] : [],
|
|
69
|
+
programAddress: params.programAddress
|
|
70
|
+
});
|
|
71
|
+
const passkeyProgramPubkey = (0, import_passkey_manager.decodeAddress)(params.programAddress);
|
|
72
|
+
const addAuthorityInstruction = (0, import_passkey_manager.encodeAddAuthorityInstruction)({
|
|
73
|
+
walletAccountIdx: ctx.walletAccountIdx,
|
|
74
|
+
authority: params.newAuthority
|
|
83
75
|
});
|
|
76
|
+
let targetProgramIdx = ctx.getAccountIndex(passkeyProgramPubkey);
|
|
77
|
+
let targetInstructionData = addAuthorityInstruction;
|
|
78
|
+
if (params.credentialId) {
|
|
79
|
+
if (!lookupSeed || !lookupAddressBytes || !lookupProof) {
|
|
80
|
+
throw new Error("Credential lookup proof data missing");
|
|
81
|
+
}
|
|
82
|
+
const registerCredentialInstruction = (0, import_passkey_manager.encodeRegisterCredentialInstruction)({
|
|
83
|
+
walletAccountIdx: ctx.walletAccountIdx,
|
|
84
|
+
lookupAccountIdx: ctx.getAccountIndex(lookupAddressBytes),
|
|
85
|
+
seed: lookupSeed,
|
|
86
|
+
stateProof: lookupProof
|
|
87
|
+
});
|
|
88
|
+
targetProgramIdx = ctx.getAccountIndex(import_multicall.MULTICALL_PROGRAM_PUBKEY);
|
|
89
|
+
targetInstructionData = (0, import_multicall.buildMulticallInstruction)([
|
|
90
|
+
{
|
|
91
|
+
programIdx: ctx.getAccountIndex(passkeyProgramPubkey),
|
|
92
|
+
instructionData: addAuthorityInstruction
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
programIdx: ctx.getAccountIndex(passkeyProgramPubkey),
|
|
96
|
+
instructionData: registerCredentialInstruction
|
|
97
|
+
}
|
|
98
|
+
]);
|
|
99
|
+
}
|
|
84
100
|
const challenge = await (0, import_passkey_manager.createValidateChallenge)(
|
|
85
101
|
parsed.nonce,
|
|
86
102
|
ctx.accountAddresses,
|
|
87
|
-
|
|
103
|
+
ctx.walletAccountIdx,
|
|
104
|
+
params.authIdx,
|
|
105
|
+
{
|
|
106
|
+
programIdx: targetProgramIdx,
|
|
107
|
+
instructionData: targetInstructionData
|
|
108
|
+
}
|
|
88
109
|
);
|
|
89
110
|
status("Waiting for passkey approval...");
|
|
90
111
|
const signature = await params.passkey.signChallenge(challenge);
|
|
91
112
|
const validateInstruction = (0, import_passkey_manager.encodeValidateInstruction)({
|
|
92
113
|
walletAccountIdx: ctx.walletAccountIdx,
|
|
93
114
|
authIdx: params.authIdx,
|
|
115
|
+
targetInstruction: {
|
|
116
|
+
programIdx: targetProgramIdx,
|
|
117
|
+
instructionData: targetInstructionData
|
|
118
|
+
},
|
|
94
119
|
signatureR: signature.signatureR,
|
|
95
120
|
signatureS: signature.signatureS,
|
|
96
121
|
authenticatorData: signature.authenticatorData,
|
|
97
122
|
clientDataJSON: signature.clientDataJSON
|
|
98
123
|
});
|
|
99
|
-
const instructionData = (0, import_passkey_manager.concatenateInstructions)([
|
|
100
|
-
validateInstruction,
|
|
101
|
-
trailingInstructionData
|
|
102
|
-
]);
|
|
103
124
|
status("Sending transaction...");
|
|
104
125
|
const result = await params.executor({
|
|
105
126
|
thru: params.thru,
|
|
106
127
|
walletSigner: params.walletSigner,
|
|
107
|
-
instructionData,
|
|
128
|
+
instructionData: validateInstruction,
|
|
108
129
|
readWriteAddresses: ctx.readWriteAddresses,
|
|
109
130
|
readOnlyAddresses: ctx.readOnlyAddresses,
|
|
110
|
-
label: params.credentialId ? "VALIDATE
|
|
131
|
+
label: params.credentialId ? "VALIDATE -> MULTICALL(ADD_AUTHORITY, REGISTER_CREDENTIAL)" : "VALIDATE -> ADD_AUTHORITY"
|
|
111
132
|
});
|
|
112
133
|
return { ...result, newAuthorityIdx };
|
|
113
134
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/auth/add-device.ts"],"sourcesContent":["/* Add-passkey-to-account transaction builder, lifted from\n `web/wallet-auth-manager/app/page.tsx` (`runValidateThen`,\n `handleSubmitAddPasskey`) so the wallet's `/embedded` post-connect\n step can reuse the exact same flow.\n\n Builds the on-chain transaction:\n VALIDATE(existingAuthority) + ADD_AUTHORITY(newPasskey) [+ REGISTER_CREDENTIAL]\n asks the caller's existing passkey to sign the challenge, then asks\n the caller's wallet signer to sign the assembled transaction, sends\n it, and returns the result. */\n\nimport {\n type Authority,\n buildAccountContext,\n concatenateInstructions,\n createValidateChallenge,\n createCredentialLookupSeed,\n deriveWalletAddress,\n encodeAddAuthorityInstruction,\n encodeRegisterCredentialInstruction,\n encodeValidateInstruction,\n parseWalletAuthorities,\n type ParsedAuthority,\n type WalletSigner,\n} from \"@thru/programs/passkey-manager\";\n\n/** Minimal shape required from a passkey signer. Both web's\n `signWithDiscoverablePasskey`/`signWithPasskey` and mobile's\n counterparts conform. */\nexport interface PasskeyChallengeSigner {\n signChallenge: (challenge: Uint8Array) => Promise<{\n signatureR: Uint8Array;\n signatureS: Uint8Array;\n authenticatorData: Uint8Array;\n clientDataJSON: Uint8Array;\n }>;\n}\n\n/** Minimal shape required from a Thru chain client. Loosely-typed\n because @thru/sdk's DTS emit is currently broken in this\n repo. The caller passes the real Thru and we narrow operationally. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyThruClient = any;\n\nexport interface AddDeviceParams {\n /** Loosely-typed Thru chain client (`@thru/sdk/client`). */\n thru: AnyThruClient;\n /** Wallet (the on-chain WalletAccount) to attach the passkey to. */\n walletAddress: string;\n /** Index of the existing authority that approves this change. Must\n currently be a passkey authority. */\n authIdx: number;\n /** New passkey to attach. tag = 1 (passkey). */\n newAuthority: Authority;\n /** Optional credential-lookup registration so the new passkey is\n discoverable on subsequent sign-ins. */\n credentialId?: Uint8Array;\n walletName?: string;\n /** Existing-passkey challenge signer (web or mobile). */\n passkey: PasskeyChallengeSigner;\n /** Wallet transaction signer that returns base64(signed bytes). */\n walletSigner: WalletSigner;\n /** Passkey program address (base58). */\n programAddress: string;\n /** Sign-and-send executor (lifted from passkey-transaction.ts in the\n wallet-auth-manager - wallet apps own this because it depends on\n the `Thru` client's transaction builder). */\n executor: TxExecutor;\n /** Optional status callback so UIs can show progress. */\n onStatus?: (message: string) => void;\n}\n\nexport interface TxExecutorParams {\n thru: AnyThruClient;\n walletSigner: WalletSigner;\n instructionData: Uint8Array;\n readWriteAddresses: string[];\n readOnlyAddresses: string[];\n label: string;\n}\n\nexport interface TxExecutorResult {\n signature: string;\n /** Loosely-typed because @thru/sdk types aren't available. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n execution: any;\n}\n\nexport type TxExecutor = (\n params: TxExecutorParams,\n) => Promise<TxExecutorResult>;\n\nexport interface AddDeviceResult extends TxExecutorResult {\n /** The new passkey's authority index after the transaction lands. */\n newAuthorityIdx: number;\n}\n\n/**\n * Run VALIDATE + ADD_AUTHORITY [+ REGISTER_CREDENTIAL] to attach a new\n * passkey to an on-chain WalletAccount.\n */\nexport async function addDeviceToAccount(\n params: AddDeviceParams,\n): Promise<AddDeviceResult> {\n const status = params.onStatus ?? (() => {});\n\n const walletAccount = await params.thru.accounts.get(params.walletAddress);\n const walletData: Uint8Array | undefined = walletAccount?.data?.data;\n if (!walletData) {\n throw new Error(\"Wallet account data missing\");\n }\n\n const parsed = parseWalletAuthorities(walletData);\n const authorizing: ParsedAuthority | undefined =\n parsed.authorities[params.authIdx];\n if (!authorizing) {\n throw new Error(\"Authorization index out of bounds\");\n }\n if (authorizing.kind !== \"passkey\") {\n throw new Error(\n \"addDeviceToAccount currently requires a passkey authority for VALIDATE\",\n );\n }\n\n /* The new authority will land at the next free slot. */\n const newAuthorityIdx = parsed.authorities.length;\n\n /* Build the instruction sequence after the VALIDATE. */\n const addAuthorityInstruction = encodeAddAuthorityInstruction({\n authority: params.newAuthority,\n });\n\n let trailingInstructionData = addAuthorityInstruction;\n let readWriteAccounts: Uint8Array[] = [];\n\n if (params.credentialId) {\n const lookupSeed = await createCredentialLookupSeed(params.credentialId);\n const lookupAddressBytes = await deriveWalletAddress(\n lookupSeed,\n params.programAddress,\n );\n\n status(\"Fetching state proof for credential lookup...\");\n const proofResult = await params.thru.proofs.generate({\n proofType: 1 /* StateProofType.CREATING */,\n address: lookupAddressBytes,\n });\n\n const ctx = buildAccountContext({\n walletAddress: params.walletAddress,\n readWriteAccounts: [lookupAddressBytes],\n readOnlyAccounts: [],\n });\n\n const registerCredentialInstruction = encodeRegisterCredentialInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n lookupAccountIdx: ctx.getAccountIndex(lookupAddressBytes),\n seed: lookupSeed,\n stateProof: proofResult.proof,\n });\n\n trailingInstructionData = concatenateInstructions([\n addAuthorityInstruction,\n registerCredentialInstruction,\n ]);\n readWriteAccounts = [lookupAddressBytes];\n }\n\n /* Build the VALIDATE challenge over (nonce, account_addresses,\n trailing_instruction_data) and ask the caller's passkey to sign. */\n const ctx = buildAccountContext({\n walletAddress: params.walletAddress,\n readWriteAccounts,\n readOnlyAccounts: [],\n });\n\n const challenge = await createValidateChallenge(\n parsed.nonce,\n ctx.accountAddresses,\n trailingInstructionData,\n );\n\n status(\"Waiting for passkey approval...\");\n const signature = await params.passkey.signChallenge(challenge);\n\n const validateInstruction = encodeValidateInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n authIdx: params.authIdx,\n signatureR: signature.signatureR,\n signatureS: signature.signatureS,\n authenticatorData: signature.authenticatorData,\n clientDataJSON: signature.clientDataJSON,\n });\n\n const instructionData = concatenateInstructions([\n validateInstruction,\n trailingInstructionData,\n ]);\n\n status(\"Sending transaction...\");\n const result = await params.executor({\n thru: params.thru,\n walletSigner: params.walletSigner,\n instructionData,\n readWriteAddresses: ctx.readWriteAddresses,\n readOnlyAddresses: ctx.readOnlyAddresses,\n label: params.credentialId\n ? \"VALIDATE + ADD_AUTHORITY + REGISTER_CREDENTIAL\"\n : \"VALIDATE + ADD_AUTHORITY\",\n });\n\n return { ...result, newAuthorityIdx };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,6BAaO;AA6EP,eAAsB,mBACpB,QAC0B;AAC1B,QAAM,SAAS,OAAO,aAAa,MAAM;AAAA,EAAC;AAE1C,QAAM,gBAAgB,MAAM,OAAO,KAAK,SAAS,IAAI,OAAO,aAAa;AACzE,QAAM,aAAqC,eAAe,MAAM;AAChE,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,6BAA6B;AAAA,EAC/C;AAEA,QAAM,aAAS,+CAAuB,UAAU;AAChD,QAAM,cACJ,OAAO,YAAY,OAAO,OAAO;AACnC,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AACA,MAAI,YAAY,SAAS,WAAW;AAClC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAGA,QAAM,kBAAkB,OAAO,YAAY;AAG3C,QAAM,8BAA0B,sDAA8B;AAAA,IAC5D,WAAW,OAAO;AAAA,EACpB,CAAC;AAED,MAAI,0BAA0B;AAC9B,MAAI,oBAAkC,CAAC;AAEvC,MAAI,OAAO,cAAc;AACvB,UAAM,aAAa,UAAM,mDAA2B,OAAO,YAAY;AACvE,UAAM,qBAAqB,UAAM;AAAA,MAC/B;AAAA,MACA,OAAO;AAAA,IACT;AAEA,WAAO,+CAA+C;AACtD,UAAM,cAAc,MAAM,OAAO,KAAK,OAAO,SAAS;AAAA,MACpD,WAAW;AAAA,MACX,SAAS;AAAA,IACX,CAAC;AAED,UAAMA,WAAM,4CAAoB;AAAA,MAC9B,eAAe,OAAO;AAAA,MACtB,mBAAmB,CAAC,kBAAkB;AAAA,MACtC,kBAAkB,CAAC;AAAA,IACrB,CAAC;AAED,UAAM,oCAAgC,4DAAoC;AAAA,MACxE,kBAAkBA,KAAI;AAAA,MACtB,kBAAkBA,KAAI,gBAAgB,kBAAkB;AAAA,MACxD,MAAM;AAAA,MACN,YAAY,YAAY;AAAA,IAC1B,CAAC;AAED,kCAA0B,gDAAwB;AAAA,MAChD;AAAA,MACA;AAAA,IACF,CAAC;AACD,wBAAoB,CAAC,kBAAkB;AAAA,EACzC;AAIA,QAAM,UAAM,4CAAoB;AAAA,IAC9B,eAAe,OAAO;AAAA,IACtB;AAAA,IACA,kBAAkB,CAAC;AAAA,EACrB,CAAC;AAED,QAAM,YAAY,UAAM;AAAA,IACtB,OAAO;AAAA,IACP,IAAI;AAAA,IACJ;AAAA,EACF;AAEA,SAAO,iCAAiC;AACxC,QAAM,YAAY,MAAM,OAAO,QAAQ,cAAc,SAAS;AAE9D,QAAM,0BAAsB,kDAA0B;AAAA,IACpD,kBAAkB,IAAI;AAAA,IACtB,SAAS,OAAO;AAAA,IAChB,YAAY,UAAU;AAAA,IACtB,YAAY,UAAU;AAAA,IACtB,mBAAmB,UAAU;AAAA,IAC7B,gBAAgB,UAAU;AAAA,EAC5B,CAAC;AAED,QAAM,sBAAkB,gDAAwB;AAAA,IAC9C;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO,wBAAwB;AAC/B,QAAM,SAAS,MAAM,OAAO,SAAS;AAAA,IACnC,MAAM,OAAO;AAAA,IACb,cAAc,OAAO;AAAA,IACrB;AAAA,IACA,oBAAoB,IAAI;AAAA,IACxB,mBAAmB,IAAI;AAAA,IACvB,OAAO,OAAO,eACV,mDACA;AAAA,EACN,CAAC;AAED,SAAO,EAAE,GAAG,QAAQ,gBAAgB;AACtC;","names":["ctx"]}
|
|
1
|
+
{"version":3,"sources":["../../src/auth/add-device.ts"],"sourcesContent":["/* Add-passkey-to-account transaction builder, lifted from\n `web/wallet-auth-manager/app/page.tsx` (`runValidateThen`,\n `handleSubmitAddPasskey`) so the wallet's `/embedded` post-connect\n step can reuse the exact same flow.\n\n Builds the on-chain transaction:\n VALIDATE(existingAuthority -> ADD_AUTHORITY(newPasskey))\n or VALIDATE(existingAuthority -> multicall[ADD_AUTHORITY, REGISTER_CREDENTIAL])\n asks the caller's existing passkey to sign the challenge, then asks\n the caller's wallet signer to sign the assembled transaction, sends\n it, and returns the result. */\n\nimport {\n type Authority,\n buildAccountContext,\n createValidateChallenge,\n createCredentialLookupSeed,\n decodeAddress,\n deriveWalletAddress,\n encodeAddAuthorityInstruction,\n encodeRegisterCredentialInstruction,\n encodeValidateInstruction,\n parseWalletAuthorities,\n type ParsedAuthority,\n type WalletSigner,\n} from \"@thru/programs/passkey-manager\";\nimport {\n MULTICALL_PROGRAM_PUBKEY,\n buildMulticallInstruction,\n} from \"@thru/programs/multicall\";\n\n/** Minimal shape required from a passkey signer. Both web's\n `signWithDiscoverablePasskey`/`signWithPasskey` and mobile's\n counterparts conform. */\nexport interface PasskeyChallengeSigner {\n signChallenge: (challenge: Uint8Array) => Promise<{\n signatureR: Uint8Array;\n signatureS: Uint8Array;\n authenticatorData: Uint8Array;\n clientDataJSON: Uint8Array;\n }>;\n}\n\n/** Minimal shape required from a Thru chain client. Loosely-typed\n because @thru/sdk's DTS emit is currently broken in this\n repo. The caller passes the real Thru and we narrow operationally. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyThruClient = any;\n\nexport interface AddDeviceParams {\n /** Loosely-typed Thru chain client (`@thru/sdk/client`). */\n thru: AnyThruClient;\n /** Wallet (the on-chain WalletAccount) to attach the passkey to. */\n walletAddress: string;\n /** Index of the existing authority that approves this change. Must\n currently be a passkey authority. */\n authIdx: number;\n /** New passkey to attach. tag = 1 (passkey). */\n newAuthority: Authority;\n /** Optional credential-lookup registration so the new passkey is\n discoverable on subsequent sign-ins. */\n credentialId?: Uint8Array;\n walletName?: string;\n /** Existing-passkey challenge signer (web or mobile). */\n passkey: PasskeyChallengeSigner;\n /** Wallet transaction signer that returns base64(signed bytes). */\n walletSigner: WalletSigner;\n /** Passkey program address (base58). */\n programAddress: string;\n /** Sign-and-send executor (lifted from passkey-transaction.ts in the\n wallet-auth-manager - wallet apps own this because it depends on\n the `Thru` client's transaction builder). */\n executor: TxExecutor;\n /** Optional status callback so UIs can show progress. */\n onStatus?: (message: string) => void;\n}\n\nexport interface TxExecutorParams {\n thru: AnyThruClient;\n walletSigner: WalletSigner;\n instructionData: Uint8Array;\n readWriteAddresses: string[];\n readOnlyAddresses: string[];\n label: string;\n}\n\nexport interface TxExecutorResult {\n signature: string;\n /** Loosely-typed because @thru/sdk types aren't available. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n execution: any;\n}\n\nexport type TxExecutor = (\n params: TxExecutorParams,\n) => Promise<TxExecutorResult>;\n\nexport interface AddDeviceResult extends TxExecutorResult {\n /** The new passkey's authority index after the transaction lands. */\n newAuthorityIdx: number;\n}\n\n/**\n * Run VALIDATE + ADD_AUTHORITY [+ REGISTER_CREDENTIAL] to attach a new\n * passkey to an on-chain WalletAccount.\n */\nexport async function addDeviceToAccount(\n params: AddDeviceParams,\n): Promise<AddDeviceResult> {\n const status = params.onStatus ?? (() => {});\n\n const walletAccount = await params.thru.accounts.get(params.walletAddress);\n const walletData: Uint8Array | undefined = walletAccount?.data?.data;\n if (!walletData) {\n throw new Error(\"Wallet account data missing\");\n }\n\n const parsed = parseWalletAuthorities(walletData);\n const authorizing: ParsedAuthority | undefined =\n parsed.authorities[params.authIdx];\n if (!authorizing) {\n throw new Error(\"Authorization index out of bounds\");\n }\n if (authorizing.kind !== \"passkey\") {\n throw new Error(\n \"addDeviceToAccount currently requires a passkey authority for VALIDATE\",\n );\n }\n\n /* The new authority will land at the next free slot. */\n const newAuthorityIdx = parsed.authorities.length;\n\n let readWriteAccounts: Uint8Array[] = [];\n let lookupSeed: Uint8Array | undefined;\n let lookupAddressBytes: Uint8Array | undefined;\n let lookupProof: Uint8Array | undefined;\n\n if (params.credentialId) {\n lookupSeed = await createCredentialLookupSeed(params.credentialId);\n lookupAddressBytes = await deriveWalletAddress(\n lookupSeed,\n params.programAddress,\n );\n\n status(\"Fetching state proof for credential lookup...\");\n const proofResult = await params.thru.proofs.generate({\n proofType: 1 /* StateProofType.CREATING */,\n address: lookupAddressBytes,\n });\n lookupProof = proofResult.proof;\n readWriteAccounts = [lookupAddressBytes];\n }\n\n const ctx = buildAccountContext({\n walletAddress: params.walletAddress,\n readWriteAccounts,\n readOnlyAccounts: params.credentialId ? [MULTICALL_PROGRAM_PUBKEY] : [],\n programAddress: params.programAddress,\n });\n\n const passkeyProgramPubkey = decodeAddress(params.programAddress);\n const addAuthorityInstruction = encodeAddAuthorityInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n authority: params.newAuthority,\n });\n\n let targetProgramIdx = ctx.getAccountIndex(passkeyProgramPubkey);\n let targetInstructionData = addAuthorityInstruction;\n\n if (params.credentialId) {\n if (!lookupSeed || !lookupAddressBytes || !lookupProof) {\n throw new Error(\"Credential lookup proof data missing\");\n }\n\n const registerCredentialInstruction = encodeRegisterCredentialInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n lookupAccountIdx: ctx.getAccountIndex(lookupAddressBytes),\n seed: lookupSeed,\n stateProof: lookupProof,\n });\n\n targetProgramIdx = ctx.getAccountIndex(MULTICALL_PROGRAM_PUBKEY);\n targetInstructionData = buildMulticallInstruction([\n {\n programIdx: ctx.getAccountIndex(passkeyProgramPubkey),\n instructionData: addAuthorityInstruction,\n },\n {\n programIdx: ctx.getAccountIndex(passkeyProgramPubkey),\n instructionData: registerCredentialInstruction,\n },\n ]);\n }\n\n /* Build the VALIDATE challenge over the target CPI and ask the caller's passkey to sign. */\n const challenge = await createValidateChallenge(\n parsed.nonce,\n ctx.accountAddresses,\n ctx.walletAccountIdx,\n params.authIdx,\n {\n programIdx: targetProgramIdx,\n instructionData: targetInstructionData,\n },\n );\n\n status(\"Waiting for passkey approval...\");\n const signature = await params.passkey.signChallenge(challenge);\n\n const validateInstruction = encodeValidateInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n authIdx: params.authIdx,\n targetInstruction: {\n programIdx: targetProgramIdx,\n instructionData: targetInstructionData,\n },\n signatureR: signature.signatureR,\n signatureS: signature.signatureS,\n authenticatorData: signature.authenticatorData,\n clientDataJSON: signature.clientDataJSON,\n });\n\n status(\"Sending transaction...\");\n const result = await params.executor({\n thru: params.thru,\n walletSigner: params.walletSigner,\n instructionData: validateInstruction,\n readWriteAddresses: ctx.readWriteAddresses,\n readOnlyAddresses: ctx.readOnlyAddresses,\n label: params.credentialId\n ? \"VALIDATE -> MULTICALL(ADD_AUTHORITY, REGISTER_CREDENTIAL)\"\n : \"VALIDATE -> ADD_AUTHORITY\",\n });\n\n return { ...result, newAuthorityIdx };\n}\n"],"mappings":"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","names":[]}
|
package/dist/auth/add-device.js
CHANGED
package/dist/auth.cjs
CHANGED
|
@@ -304,6 +304,7 @@ async function executePasskeyTransaction(opts) {
|
|
|
304
304
|
|
|
305
305
|
// src/auth/add-device.ts
|
|
306
306
|
var import_passkey_manager3 = require("@thru/programs/passkey-manager");
|
|
307
|
+
var import_multicall = require("@thru/programs/multicall");
|
|
307
308
|
async function addDeviceToAccount(params) {
|
|
308
309
|
const status = params.onStatus ?? (() => {
|
|
309
310
|
});
|
|
@@ -323,14 +324,13 @@ async function addDeviceToAccount(params) {
|
|
|
323
324
|
);
|
|
324
325
|
}
|
|
325
326
|
const newAuthorityIdx = parsed.authorities.length;
|
|
326
|
-
const addAuthorityInstruction = (0, import_passkey_manager3.encodeAddAuthorityInstruction)({
|
|
327
|
-
authority: params.newAuthority
|
|
328
|
-
});
|
|
329
|
-
let trailingInstructionData = addAuthorityInstruction;
|
|
330
327
|
let readWriteAccounts = [];
|
|
328
|
+
let lookupSeed;
|
|
329
|
+
let lookupAddressBytes;
|
|
330
|
+
let lookupProof;
|
|
331
331
|
if (params.credentialId) {
|
|
332
|
-
|
|
333
|
-
|
|
332
|
+
lookupSeed = await (0, import_passkey_manager3.createCredentialLookupSeed)(params.credentialId);
|
|
333
|
+
lookupAddressBytes = await (0, import_passkey_manager3.deriveWalletAddress)(
|
|
334
334
|
lookupSeed,
|
|
335
335
|
params.programAddress
|
|
336
336
|
);
|
|
@@ -339,55 +339,76 @@ async function addDeviceToAccount(params) {
|
|
|
339
339
|
proofType: 1,
|
|
340
340
|
address: lookupAddressBytes
|
|
341
341
|
});
|
|
342
|
-
|
|
343
|
-
walletAddress: params.walletAddress,
|
|
344
|
-
readWriteAccounts: [lookupAddressBytes],
|
|
345
|
-
readOnlyAccounts: []
|
|
346
|
-
});
|
|
347
|
-
const registerCredentialInstruction = (0, import_passkey_manager3.encodeRegisterCredentialInstruction)({
|
|
348
|
-
walletAccountIdx: ctx2.walletAccountIdx,
|
|
349
|
-
lookupAccountIdx: ctx2.getAccountIndex(lookupAddressBytes),
|
|
350
|
-
seed: lookupSeed,
|
|
351
|
-
stateProof: proofResult.proof
|
|
352
|
-
});
|
|
353
|
-
trailingInstructionData = (0, import_passkey_manager3.concatenateInstructions)([
|
|
354
|
-
addAuthorityInstruction,
|
|
355
|
-
registerCredentialInstruction
|
|
356
|
-
]);
|
|
342
|
+
lookupProof = proofResult.proof;
|
|
357
343
|
readWriteAccounts = [lookupAddressBytes];
|
|
358
344
|
}
|
|
359
345
|
const ctx = (0, import_passkey_manager3.buildAccountContext)({
|
|
360
346
|
walletAddress: params.walletAddress,
|
|
361
347
|
readWriteAccounts,
|
|
362
|
-
readOnlyAccounts: []
|
|
348
|
+
readOnlyAccounts: params.credentialId ? [import_multicall.MULTICALL_PROGRAM_PUBKEY] : [],
|
|
349
|
+
programAddress: params.programAddress
|
|
350
|
+
});
|
|
351
|
+
const passkeyProgramPubkey = (0, import_passkey_manager3.decodeAddress)(params.programAddress);
|
|
352
|
+
const addAuthorityInstruction = (0, import_passkey_manager3.encodeAddAuthorityInstruction)({
|
|
353
|
+
walletAccountIdx: ctx.walletAccountIdx,
|
|
354
|
+
authority: params.newAuthority
|
|
363
355
|
});
|
|
356
|
+
let targetProgramIdx = ctx.getAccountIndex(passkeyProgramPubkey);
|
|
357
|
+
let targetInstructionData = addAuthorityInstruction;
|
|
358
|
+
if (params.credentialId) {
|
|
359
|
+
if (!lookupSeed || !lookupAddressBytes || !lookupProof) {
|
|
360
|
+
throw new Error("Credential lookup proof data missing");
|
|
361
|
+
}
|
|
362
|
+
const registerCredentialInstruction = (0, import_passkey_manager3.encodeRegisterCredentialInstruction)({
|
|
363
|
+
walletAccountIdx: ctx.walletAccountIdx,
|
|
364
|
+
lookupAccountIdx: ctx.getAccountIndex(lookupAddressBytes),
|
|
365
|
+
seed: lookupSeed,
|
|
366
|
+
stateProof: lookupProof
|
|
367
|
+
});
|
|
368
|
+
targetProgramIdx = ctx.getAccountIndex(import_multicall.MULTICALL_PROGRAM_PUBKEY);
|
|
369
|
+
targetInstructionData = (0, import_multicall.buildMulticallInstruction)([
|
|
370
|
+
{
|
|
371
|
+
programIdx: ctx.getAccountIndex(passkeyProgramPubkey),
|
|
372
|
+
instructionData: addAuthorityInstruction
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
programIdx: ctx.getAccountIndex(passkeyProgramPubkey),
|
|
376
|
+
instructionData: registerCredentialInstruction
|
|
377
|
+
}
|
|
378
|
+
]);
|
|
379
|
+
}
|
|
364
380
|
const challenge = await (0, import_passkey_manager3.createValidateChallenge)(
|
|
365
381
|
parsed.nonce,
|
|
366
382
|
ctx.accountAddresses,
|
|
367
|
-
|
|
383
|
+
ctx.walletAccountIdx,
|
|
384
|
+
params.authIdx,
|
|
385
|
+
{
|
|
386
|
+
programIdx: targetProgramIdx,
|
|
387
|
+
instructionData: targetInstructionData
|
|
388
|
+
}
|
|
368
389
|
);
|
|
369
390
|
status("Waiting for passkey approval...");
|
|
370
391
|
const signature = await params.passkey.signChallenge(challenge);
|
|
371
392
|
const validateInstruction = (0, import_passkey_manager3.encodeValidateInstruction)({
|
|
372
393
|
walletAccountIdx: ctx.walletAccountIdx,
|
|
373
394
|
authIdx: params.authIdx,
|
|
395
|
+
targetInstruction: {
|
|
396
|
+
programIdx: targetProgramIdx,
|
|
397
|
+
instructionData: targetInstructionData
|
|
398
|
+
},
|
|
374
399
|
signatureR: signature.signatureR,
|
|
375
400
|
signatureS: signature.signatureS,
|
|
376
401
|
authenticatorData: signature.authenticatorData,
|
|
377
402
|
clientDataJSON: signature.clientDataJSON
|
|
378
403
|
});
|
|
379
|
-
const instructionData = (0, import_passkey_manager3.concatenateInstructions)([
|
|
380
|
-
validateInstruction,
|
|
381
|
-
trailingInstructionData
|
|
382
|
-
]);
|
|
383
404
|
status("Sending transaction...");
|
|
384
405
|
const result = await params.executor({
|
|
385
406
|
thru: params.thru,
|
|
386
407
|
walletSigner: params.walletSigner,
|
|
387
|
-
instructionData,
|
|
408
|
+
instructionData: validateInstruction,
|
|
388
409
|
readWriteAddresses: ctx.readWriteAddresses,
|
|
389
410
|
readOnlyAddresses: ctx.readOnlyAddresses,
|
|
390
|
-
label: params.credentialId ? "VALIDATE
|
|
411
|
+
label: params.credentialId ? "VALIDATE -> MULTICALL(ADD_AUTHORITY, REGISTER_CREDENTIAL)" : "VALIDATE -> ADD_AUTHORITY"
|
|
391
412
|
});
|
|
392
413
|
return { ...result, newAuthorityIdx };
|
|
393
414
|
}
|
package/dist/auth.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth/index.ts","../src/auth/execute-tx.ts","../src/mobile/passkey.ts","../src/mobile/storage.ts","../src/auth/add-device.ts","../src/auth/use-passkey-auth.ts","../src/mobile/errors.ts"],"sourcesContent":["export type {\n PasskeyApiUser,\n PasskeyAuthApiResponse,\n PasskeyUser,\n PasskeyAuthConfig,\n PasskeyAuthState,\n PasskeyAuthActions,\n PasskeyAuthStore,\n PasskeyAuthBoundStore,\n} from './types';\n\nexport { executePasskeyTransaction } from './execute-tx';\n\nexport { addDeviceToAccount } from './add-device';\nexport type {\n AddDeviceParams,\n AddDeviceResult,\n AnyThruClient,\n PasskeyChallengeSigner,\n TxExecutor,\n TxExecutorParams,\n TxExecutorResult,\n} from './add-device';\n\nexport {\n createPasskeyAuthStore,\n getPasskeyAuthStore,\n usePasskeyAuth,\n} from './use-passkey-auth';\n","import { base64UrlToBytes, bytesToBase64, bytesToHex } from '@thru/programs/passkey-manager';\nimport { signWithPasskey } from '../mobile/passkey';\nimport { touchPasskeyLastUsedAt } from '../mobile/storage';\n\nasync function readJson(response: Response): Promise<Record<string, unknown>> {\n try {\n return (await response.json()) as Record<string, unknown>;\n } catch {\n throw new Error(`Non-JSON response (HTTP ${response.status})`);\n }\n}\n\nexport async function executePasskeyTransaction<\n P extends Record<string, unknown>,\n R\n>(opts: {\n challengeUrl: string;\n submitUrl: string;\n params: P;\n credentialId: string;\n rpId: string;\n}): Promise<R> {\n let challengeRes: Response;\n try {\n challengeRes = await fetch(opts.challengeUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(opts.params),\n });\n } catch {\n throw new Error('Network request failed (challenge)');\n }\n\n const challengeData = await readJson(challengeRes);\n if (!challengeRes.ok || challengeData.success !== true) {\n throw new Error(\n typeof challengeData.error === 'string'\n ? challengeData.error\n : 'Failed to get challenge'\n );\n }\n\n if (typeof challengeData.challenge !== 'string') {\n throw new Error('Challenge response did not include a challenge');\n }\n\n const challengeBytes = base64UrlToBytes(challengeData.challenge);\n const signature = await signWithPasskey(\n opts.credentialId,\n challengeBytes,\n opts.rpId\n );\n await touchPasskeyLastUsedAt().catch((error) => {\n console.warn('Failed to update passkey last-used timestamp:', error);\n });\n\n const { success: _success, error: _error, ...challengeFields } = challengeData;\n\n let submitRes: Response;\n try {\n submitRes = await fetch(opts.submitUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...opts.params,\n ...challengeFields,\n signatureR: bytesToHex(signature.signatureR),\n signatureS: bytesToHex(signature.signatureS),\n authenticatorData: bytesToBase64(signature.authenticatorData),\n clientDataJSON: bytesToBase64(signature.clientDataJSON),\n }),\n });\n } catch {\n throw new Error('Network request failed (submit)');\n }\n\n const submitData = await readJson(submitRes);\n if (!submitRes.ok || submitData.success !== true) {\n throw new Error(\n typeof submitData.error === 'string'\n ? submitData.error\n : 'Failed to submit transaction'\n );\n }\n\n return submitData as R;\n}\n","import { create as passkeyCreate, get as passkeyGet } from 'react-native-passkeys';\nimport {\n bytesToBase64Url,\n base64UrlToBytes,\n normalizeLowS,\n parseDerSignature,\n type PasskeySigningResult,\n} from '@thru/programs/passkey-manager';\nimport type {\n DiscoverablePasskeyResult,\n PasskeyMobileConfig,\n PasskeyRegistrationResult,\n} from './types';\n\ntype ProcessLike = typeof globalThis & {\n process?: {\n env?: Record<string, string | undefined>;\n };\n};\n\nfunction getDefaultConfig(config?: PasskeyMobileConfig): Required<PasskeyMobileConfig> {\n const env = (globalThis as ProcessLike).process?.env ?? {};\n\n return {\n rpId: config?.rpId ?? env.EXPO_PUBLIC_PASSKEY_RP_ID ?? 'wallet.thru.org',\n rpName: config?.rpName ?? env.EXPO_PUBLIC_PASSKEY_RP_NAME ?? 'Thru Wallet',\n };\n}\n\nexport async function registerPasskey(\n alias: string,\n userId: string,\n config?: PasskeyMobileConfig\n): Promise<PasskeyRegistrationResult> {\n const { rpId, rpName } = getDefaultConfig(config);\n const challenge = bytesToBase64Url(crypto.getRandomValues(new Uint8Array(32)));\n const userIdB64 = bytesToBase64Url(new TextEncoder().encode(userId));\n\n const result = await passkeyCreate({\n challenge,\n rp: { id: rpId, name: rpName },\n user: { id: userIdB64, name: alias, displayName: alias },\n pubKeyCredParams: [{ type: 'public-key', alg: -7 }],\n authenticatorSelection: {\n authenticatorAttachment: 'platform',\n userVerification: 'required',\n residentKey: 'required',\n },\n attestation: 'none',\n timeout: 60000,\n });\n\n if (!result) {\n throw new Error('Passkey registration was cancelled');\n }\n\n const publicKeyB64 = result.response.getPublicKey?.();\n if (!publicKeyB64) {\n throw new Error('Failed to retrieve public key from registration');\n }\n\n const keyBytes = base64UrlToBytes(publicKeyB64);\n const { x, y } = extractP256Coordinates(keyBytes);\n\n return {\n credentialId: result.id,\n publicKeyX: x,\n publicKeyY: y,\n rpId,\n };\n}\n\nexport async function signWithPasskey(\n credentialId: string,\n challenge: Uint8Array,\n rpId?: string\n): Promise<PasskeySigningResult> {\n const resolvedRpId = rpId ?? getDefaultConfig().rpId;\n const challengeB64 = bytesToBase64Url(challenge);\n\n const result = await passkeyGet({\n challenge: challengeB64,\n rpId: resolvedRpId,\n allowCredentials: [{ type: 'public-key', id: credentialId }],\n userVerification: 'required',\n timeout: 60000,\n });\n\n if (!result) {\n throw new Error('Passkey authentication was cancelled');\n }\n\n const derSignature = base64UrlToBytes(result.response.signature);\n let { r, s } = parseDerSignature(derSignature);\n s = normalizeLowS(s);\n\n return {\n signature: new Uint8Array([...r, ...s]),\n authenticatorData: base64UrlToBytes(result.response.authenticatorData),\n clientDataJSON: base64UrlToBytes(result.response.clientDataJSON),\n signatureR: r,\n signatureS: s,\n };\n}\n\nexport async function authenticateWithDiscoverablePasskey(\n config?: Pick<PasskeyMobileConfig, 'rpId'>\n): Promise<DiscoverablePasskeyResult | null> {\n const { rpId } = getDefaultConfig(config);\n\n try {\n const challenge = bytesToBase64Url(crypto.getRandomValues(new Uint8Array(32)));\n const result = await passkeyGet({\n challenge,\n rpId,\n userVerification: 'required',\n timeout: 60000,\n });\n\n return result ? { credentialId: result.id, rpId } : null;\n } catch {\n return null;\n }\n}\n\nexport function extractP256Coordinates(\n keyBytes: Uint8Array\n): { x: Uint8Array; y: Uint8Array } {\n if (keyBytes.length === 64) {\n return {\n x: keyBytes.slice(0, 32),\n y: keyBytes.slice(32, 64),\n };\n }\n\n if (keyBytes.length === 65 && keyBytes[0] === 0x04) {\n return {\n x: keyBytes.slice(1, 33),\n y: keyBytes.slice(33, 65),\n };\n }\n\n const pointStart = keyBytes.length - 65;\n if (pointStart > 0 && keyBytes[pointStart] === 0x04) {\n return {\n x: keyBytes.slice(pointStart + 1, pointStart + 33),\n y: keyBytes.slice(pointStart + 33, pointStart + 65),\n };\n }\n\n throw new Error(\n `Unsupported public key format (${keyBytes.length} bytes). Expected raw X||Y (64), uncompressed point (65), or SPKI DER (91).`\n );\n}\n","import * as SecureStore from 'expo-secure-store';\nimport type { PasskeyMetadata } from '@thru/programs/passkey-manager';\n\nconst SECURE_STORE_OPTS = {\n keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,\n} as const;\n\nconst PASSKEY_CREDENTIAL_ID_KEY = 'thru_passkey_credential_id';\nconst PASSKEY_PUBLIC_KEY_X_KEY = 'thru_passkey_pubkey_x';\nconst PASSKEY_PUBLIC_KEY_Y_KEY = 'thru_passkey_pubkey_y';\nconst PASSKEY_RP_ID_KEY = 'thru_passkey_rp_id';\nconst PASSKEY_LABEL_KEY = 'thru_passkey_label';\nconst PASSKEY_CREATED_AT_KEY = 'thru_passkey_created_at';\nconst PASSKEY_LAST_USED_AT_KEY = 'thru_passkey_last_used_at';\n\nconst ADDRESS_KEY = 'thru_address';\nconst USER_ID_KEY = 'thru_user_id';\nconst TOKEN_ACCOUNT_KEY = 'thru_token_account';\n\nexport async function storePasskeyMetadata(metadata: PasskeyMetadata): Promise<void> {\n await Promise.all([\n SecureStore.setItemAsync(PASSKEY_CREDENTIAL_ID_KEY, metadata.credentialId, SECURE_STORE_OPTS),\n SecureStore.setItemAsync(PASSKEY_PUBLIC_KEY_X_KEY, metadata.publicKeyX, SECURE_STORE_OPTS),\n SecureStore.setItemAsync(PASSKEY_PUBLIC_KEY_Y_KEY, metadata.publicKeyY, SECURE_STORE_OPTS),\n SecureStore.setItemAsync(PASSKEY_RP_ID_KEY, metadata.rpId, SECURE_STORE_OPTS),\n SecureStore.setItemAsync(PASSKEY_LABEL_KEY, metadata.label ?? '', SECURE_STORE_OPTS),\n SecureStore.setItemAsync(PASSKEY_CREATED_AT_KEY, metadata.createdAt, SECURE_STORE_OPTS),\n SecureStore.setItemAsync(PASSKEY_LAST_USED_AT_KEY, metadata.lastUsedAt, SECURE_STORE_OPTS),\n ]);\n}\n\nexport async function getStoredPasskeyMetadata(): Promise<PasskeyMetadata | null> {\n const credentialId = await SecureStore.getItemAsync(PASSKEY_CREDENTIAL_ID_KEY);\n if (!credentialId) return null;\n\n const [publicKeyX, publicKeyY, rpId, label, createdAt, lastUsedAt] = await Promise.all([\n SecureStore.getItemAsync(PASSKEY_PUBLIC_KEY_X_KEY),\n SecureStore.getItemAsync(PASSKEY_PUBLIC_KEY_Y_KEY),\n SecureStore.getItemAsync(PASSKEY_RP_ID_KEY),\n SecureStore.getItemAsync(PASSKEY_LABEL_KEY),\n SecureStore.getItemAsync(PASSKEY_CREATED_AT_KEY),\n SecureStore.getItemAsync(PASSKEY_LAST_USED_AT_KEY),\n ]);\n\n if (!rpId || !createdAt) return null;\n\n return {\n credentialId,\n publicKeyX: publicKeyX ?? '',\n publicKeyY: publicKeyY ?? '',\n rpId,\n label: label || undefined,\n createdAt,\n lastUsedAt: lastUsedAt ?? createdAt,\n };\n}\n\nexport async function touchPasskeyLastUsedAt(lastUsedAt = new Date().toISOString()): Promise<string> {\n await SecureStore.setItemAsync(PASSKEY_LAST_USED_AT_KEY, lastUsedAt, SECURE_STORE_OPTS);\n return lastUsedAt;\n}\n\nexport async function hasStoredPasskey(): Promise<boolean> {\n return (await SecureStore.getItemAsync(PASSKEY_CREDENTIAL_ID_KEY)) !== null;\n}\n\nexport async function clearPasskeyMetadata(): Promise<void> {\n await Promise.all([\n SecureStore.deleteItemAsync(PASSKEY_CREDENTIAL_ID_KEY),\n SecureStore.deleteItemAsync(PASSKEY_PUBLIC_KEY_X_KEY),\n SecureStore.deleteItemAsync(PASSKEY_PUBLIC_KEY_Y_KEY),\n SecureStore.deleteItemAsync(PASSKEY_RP_ID_KEY),\n SecureStore.deleteItemAsync(PASSKEY_LABEL_KEY),\n SecureStore.deleteItemAsync(PASSKEY_CREATED_AT_KEY),\n SecureStore.deleteItemAsync(PASSKEY_LAST_USED_AT_KEY),\n ]);\n}\n\nexport async function storeWalletInfo(\n address: string,\n userId: string,\n tokenAccountAddress?: string\n): Promise<void> {\n await Promise.all([\n SecureStore.setItemAsync(ADDRESS_KEY, address, SECURE_STORE_OPTS),\n SecureStore.setItemAsync(USER_ID_KEY, userId, SECURE_STORE_OPTS),\n tokenAccountAddress\n ? SecureStore.setItemAsync(TOKEN_ACCOUNT_KEY, tokenAccountAddress, SECURE_STORE_OPTS)\n : SecureStore.deleteItemAsync(TOKEN_ACCOUNT_KEY),\n ]);\n}\n\nexport async function hasStoredWallet(): Promise<boolean> {\n return (await SecureStore.getItemAsync(ADDRESS_KEY)) !== null;\n}\n\nexport async function getStoredAddress(): Promise<string | null> {\n return SecureStore.getItemAsync(ADDRESS_KEY);\n}\n\nexport async function getStoredUserId(): Promise<string | null> {\n return SecureStore.getItemAsync(USER_ID_KEY);\n}\n\nexport async function getStoredTokenAccount(): Promise<string | null> {\n return SecureStore.getItemAsync(TOKEN_ACCOUNT_KEY);\n}\n\nexport async function clearSession(): Promise<void> {\n await Promise.all([\n SecureStore.deleteItemAsync(ADDRESS_KEY),\n SecureStore.deleteItemAsync(USER_ID_KEY),\n SecureStore.deleteItemAsync(TOKEN_ACCOUNT_KEY),\n ]);\n}\n","/* Add-passkey-to-account transaction builder, lifted from\n `web/wallet-auth-manager/app/page.tsx` (`runValidateThen`,\n `handleSubmitAddPasskey`) so the wallet's `/embedded` post-connect\n step can reuse the exact same flow.\n\n Builds the on-chain transaction:\n VALIDATE(existingAuthority) + ADD_AUTHORITY(newPasskey) [+ REGISTER_CREDENTIAL]\n asks the caller's existing passkey to sign the challenge, then asks\n the caller's wallet signer to sign the assembled transaction, sends\n it, and returns the result. */\n\nimport {\n type Authority,\n buildAccountContext,\n concatenateInstructions,\n createValidateChallenge,\n createCredentialLookupSeed,\n deriveWalletAddress,\n encodeAddAuthorityInstruction,\n encodeRegisterCredentialInstruction,\n encodeValidateInstruction,\n parseWalletAuthorities,\n type ParsedAuthority,\n type WalletSigner,\n} from \"@thru/programs/passkey-manager\";\n\n/** Minimal shape required from a passkey signer. Both web's\n `signWithDiscoverablePasskey`/`signWithPasskey` and mobile's\n counterparts conform. */\nexport interface PasskeyChallengeSigner {\n signChallenge: (challenge: Uint8Array) => Promise<{\n signatureR: Uint8Array;\n signatureS: Uint8Array;\n authenticatorData: Uint8Array;\n clientDataJSON: Uint8Array;\n }>;\n}\n\n/** Minimal shape required from a Thru chain client. Loosely-typed\n because @thru/sdk's DTS emit is currently broken in this\n repo. The caller passes the real Thru and we narrow operationally. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyThruClient = any;\n\nexport interface AddDeviceParams {\n /** Loosely-typed Thru chain client (`@thru/sdk/client`). */\n thru: AnyThruClient;\n /** Wallet (the on-chain WalletAccount) to attach the passkey to. */\n walletAddress: string;\n /** Index of the existing authority that approves this change. Must\n currently be a passkey authority. */\n authIdx: number;\n /** New passkey to attach. tag = 1 (passkey). */\n newAuthority: Authority;\n /** Optional credential-lookup registration so the new passkey is\n discoverable on subsequent sign-ins. */\n credentialId?: Uint8Array;\n walletName?: string;\n /** Existing-passkey challenge signer (web or mobile). */\n passkey: PasskeyChallengeSigner;\n /** Wallet transaction signer that returns base64(signed bytes). */\n walletSigner: WalletSigner;\n /** Passkey program address (base58). */\n programAddress: string;\n /** Sign-and-send executor (lifted from passkey-transaction.ts in the\n wallet-auth-manager - wallet apps own this because it depends on\n the `Thru` client's transaction builder). */\n executor: TxExecutor;\n /** Optional status callback so UIs can show progress. */\n onStatus?: (message: string) => void;\n}\n\nexport interface TxExecutorParams {\n thru: AnyThruClient;\n walletSigner: WalletSigner;\n instructionData: Uint8Array;\n readWriteAddresses: string[];\n readOnlyAddresses: string[];\n label: string;\n}\n\nexport interface TxExecutorResult {\n signature: string;\n /** Loosely-typed because @thru/sdk types aren't available. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n execution: any;\n}\n\nexport type TxExecutor = (\n params: TxExecutorParams,\n) => Promise<TxExecutorResult>;\n\nexport interface AddDeviceResult extends TxExecutorResult {\n /** The new passkey's authority index after the transaction lands. */\n newAuthorityIdx: number;\n}\n\n/**\n * Run VALIDATE + ADD_AUTHORITY [+ REGISTER_CREDENTIAL] to attach a new\n * passkey to an on-chain WalletAccount.\n */\nexport async function addDeviceToAccount(\n params: AddDeviceParams,\n): Promise<AddDeviceResult> {\n const status = params.onStatus ?? (() => {});\n\n const walletAccount = await params.thru.accounts.get(params.walletAddress);\n const walletData: Uint8Array | undefined = walletAccount?.data?.data;\n if (!walletData) {\n throw new Error(\"Wallet account data missing\");\n }\n\n const parsed = parseWalletAuthorities(walletData);\n const authorizing: ParsedAuthority | undefined =\n parsed.authorities[params.authIdx];\n if (!authorizing) {\n throw new Error(\"Authorization index out of bounds\");\n }\n if (authorizing.kind !== \"passkey\") {\n throw new Error(\n \"addDeviceToAccount currently requires a passkey authority for VALIDATE\",\n );\n }\n\n /* The new authority will land at the next free slot. */\n const newAuthorityIdx = parsed.authorities.length;\n\n /* Build the instruction sequence after the VALIDATE. */\n const addAuthorityInstruction = encodeAddAuthorityInstruction({\n authority: params.newAuthority,\n });\n\n let trailingInstructionData = addAuthorityInstruction;\n let readWriteAccounts: Uint8Array[] = [];\n\n if (params.credentialId) {\n const lookupSeed = await createCredentialLookupSeed(params.credentialId);\n const lookupAddressBytes = await deriveWalletAddress(\n lookupSeed,\n params.programAddress,\n );\n\n status(\"Fetching state proof for credential lookup...\");\n const proofResult = await params.thru.proofs.generate({\n proofType: 1 /* StateProofType.CREATING */,\n address: lookupAddressBytes,\n });\n\n const ctx = buildAccountContext({\n walletAddress: params.walletAddress,\n readWriteAccounts: [lookupAddressBytes],\n readOnlyAccounts: [],\n });\n\n const registerCredentialInstruction = encodeRegisterCredentialInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n lookupAccountIdx: ctx.getAccountIndex(lookupAddressBytes),\n seed: lookupSeed,\n stateProof: proofResult.proof,\n });\n\n trailingInstructionData = concatenateInstructions([\n addAuthorityInstruction,\n registerCredentialInstruction,\n ]);\n readWriteAccounts = [lookupAddressBytes];\n }\n\n /* Build the VALIDATE challenge over (nonce, account_addresses,\n trailing_instruction_data) and ask the caller's passkey to sign. */\n const ctx = buildAccountContext({\n walletAddress: params.walletAddress,\n readWriteAccounts,\n readOnlyAccounts: [],\n });\n\n const challenge = await createValidateChallenge(\n parsed.nonce,\n ctx.accountAddresses,\n trailingInstructionData,\n );\n\n status(\"Waiting for passkey approval...\");\n const signature = await params.passkey.signChallenge(challenge);\n\n const validateInstruction = encodeValidateInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n authIdx: params.authIdx,\n signatureR: signature.signatureR,\n signatureS: signature.signatureS,\n authenticatorData: signature.authenticatorData,\n clientDataJSON: signature.clientDataJSON,\n });\n\n const instructionData = concatenateInstructions([\n validateInstruction,\n trailingInstructionData,\n ]);\n\n status(\"Sending transaction...\");\n const result = await params.executor({\n thru: params.thru,\n walletSigner: params.walletSigner,\n instructionData,\n readWriteAddresses: ctx.readWriteAddresses,\n readOnlyAddresses: ctx.readOnlyAddresses,\n label: params.credentialId\n ? \"VALIDATE + ADD_AUTHORITY + REGISTER_CREDENTIAL\"\n : \"VALIDATE + ADD_AUTHORITY\",\n });\n\n return { ...result, newAuthorityIdx };\n}\n","import { bytesToHex } from '@thru/programs/passkey-manager';\nimport { create } from 'zustand';\nimport { classifyPasskeyError } from '../mobile/errors';\nimport {\n authenticateWithDiscoverablePasskey,\n registerPasskey,\n signWithPasskey,\n} from '../mobile/passkey';\nimport {\n clearPasskeyMetadata,\n clearSession,\n getStoredAddress,\n getStoredPasskeyMetadata,\n getStoredUserId,\n hasStoredPasskey,\n hasStoredWallet,\n storePasskeyMetadata,\n storeWalletInfo,\n touchPasskeyLastUsedAt,\n} from '../mobile/storage';\nimport type {\n PasskeyAuthApiResponse,\n PasskeyAuthBoundStore,\n PasskeyAuthConfig,\n PasskeyAuthStore,\n PasskeyUser,\n} from './types';\n\nconst storeCache = new Map<string, PasskeyAuthBoundStore<any>>();\n\nfunction createStoreKey(config: PasskeyAuthConfig): string {\n return [config.apiUrl, config.alias ?? '', config.rpId ?? '', config.rpName ?? ''].join('::');\n}\n\nfunction buildDisplayName(address: string): string {\n return `${address.slice(0, 8)}...${address.slice(-4)}`;\n}\n\nfunction toPasskeyUser<TExtra>(\n user: PasskeyAuthApiResponse<TExtra>['user']\n): PasskeyUser<TExtra> {\n return {\n id: user.id,\n displayName: buildDisplayName(user.publicKey),\n tokenAccountAddress: user.tokenAccountAddress ?? null,\n extras: user.extras,\n };\n}\n\nasync function readJson<T>(response: Response): Promise<T> {\n return (await response.json()) as T;\n}\n\nasync function postJson<T>(\n url: string,\n body: Record<string, unknown>\n): Promise<T> {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n const data = await readJson<Record<string, unknown>>(response);\n if (!response.ok || data.success !== true) {\n throw new Error(\n typeof data.error === 'string' ? data.error : 'Request failed'\n );\n }\n\n return data as unknown as T;\n}\n\nasync function getCurrentUser<TExtra>(\n apiUrl: string,\n address: string\n): Promise<PasskeyAuthApiResponse<TExtra> | null> {\n const response = await fetch(`${apiUrl}/auth/me`, {\n headers: {\n 'Content-Type': 'application/json',\n 'x-wallet-address': address,\n },\n });\n\n if (response.status === 404) return null;\n\n const data = await readJson<Record<string, unknown>>(response);\n if (!response.ok || data.success !== true) {\n throw new Error(\n typeof data.error === 'string' ? data.error : 'Failed to fetch current user'\n );\n }\n\n return data as unknown as PasskeyAuthApiResponse<TExtra>;\n}\n\nexport function createPasskeyAuthStore<TExtra = Record<string, never>>(\n config: PasskeyAuthConfig\n): PasskeyAuthBoundStore<TExtra> {\n const resolvedAlias = config.alias ?? 'Thru Wallet';\n\n return create<PasskeyAuthStore<TExtra>>((set, get) => ({\n isAuthenticated: false,\n isLoading: false,\n isInitialized: false,\n hasExistingPasskey: false,\n needsNewPasskey: false,\n error: null,\n user: null,\n address: null,\n activeCredentialId: null,\n\n initialize: async () => {\n try {\n const hasPasskey = await hasStoredPasskey();\n\n if (hasPasskey) {\n const storedAddress = await getStoredAddress();\n if (storedAddress) {\n const user = await Promise.race([\n getCurrentUser<TExtra>(config.apiUrl, storedAddress),\n new Promise<null>((_, reject) =>\n setTimeout(() => reject(new Error('timeout')), 2000)\n ),\n ]).catch(() => undefined);\n\n if (user === null) {\n await clearSession();\n await clearPasskeyMetadata();\n set({ isInitialized: true, hasExistingPasskey: false });\n return;\n }\n } else {\n const metadata = await getStoredPasskeyMetadata();\n if (metadata && !metadata.publicKeyX && !metadata.publicKeyY) {\n await clearPasskeyMetadata();\n set({ isInitialized: true, hasExistingPasskey: false });\n return;\n }\n }\n }\n\n set({ isInitialized: true, hasExistingPasskey: hasPasskey });\n } catch (error) {\n console.error('Failed to initialize passkey auth:', error);\n set({ isInitialized: true, error: 'Failed to initialize wallet' });\n }\n },\n\n createWallet: async () => {\n set({ isLoading: true, error: null, needsNewPasskey: false });\n\n try {\n const tempId = `user-${Date.now()}`;\n const passkeyLabel = resolvedAlias.trim() || 'Thru Wallet';\n const { credentialId, publicKeyX, publicKeyY, rpId } =\n await registerPasskey(passkeyLabel, tempId, {\n rpId: config.rpId,\n rpName: config.rpName,\n });\n\n const now = new Date().toISOString();\n const pubkeyXHex = bytesToHex(publicKeyX);\n const pubkeyYHex = bytesToHex(publicKeyY);\n\n await storePasskeyMetadata({\n credentialId,\n publicKeyX: pubkeyXHex,\n publicKeyY: pubkeyYHex,\n rpId,\n label: passkeyLabel,\n createdAt: now,\n lastUsedAt: now,\n });\n\n const response = await postJson<PasskeyAuthApiResponse<TExtra>>(\n `${config.apiUrl}/auth/register-passkey-wallet`,\n {\n pubkeyX: pubkeyXHex,\n pubkeyY: pubkeyYHex,\n credentialId,\n }\n );\n\n const walletAddress = response.user.publicKey;\n await storeWalletInfo(\n walletAddress,\n response.user.id,\n response.user.tokenAccountAddress ?? undefined\n );\n\n set({\n isLoading: false,\n isAuthenticated: true,\n hasExistingPasskey: true,\n activeCredentialId: credentialId,\n address: walletAddress,\n user: toPasskeyUser(response.user),\n });\n\n return true;\n } catch (error) {\n if (classifyPasskeyError(error) === 'USER_CANCELLED') {\n set({ isLoading: false });\n return false;\n }\n\n console.error('Failed to create passkey wallet:', error);\n set({\n isLoading: false,\n error: error instanceof Error ? error.message : 'Failed to create wallet',\n });\n return false;\n }\n },\n\n unlockWithPasskey: async () => {\n set({ isLoading: true, error: null, needsNewPasskey: false });\n\n try {\n const metadata = await getStoredPasskeyMetadata();\n if (!metadata) throw new Error('No stored passkey found');\n\n await signWithPasskey(\n metadata.credentialId,\n crypto.getRandomValues(new Uint8Array(32)),\n metadata.rpId\n );\n await touchPasskeyLastUsedAt().catch((error) => {\n console.warn('Failed to update passkey last-used timestamp:', error);\n });\n\n const hasWallet = await hasStoredWallet();\n let walletAddress: string;\n let userId: string;\n let tokenAccountAddress: string | undefined;\n let response: PasskeyAuthApiResponse<TExtra> | null = null;\n\n if (hasWallet) {\n const storedAddress = await getStoredAddress();\n const storedUserId = await getStoredUserId();\n if (!storedAddress || !storedUserId) {\n throw new Error('Incomplete wallet data');\n }\n\n walletAddress = storedAddress;\n userId = storedUserId;\n\n const current = await getCurrentUser<TExtra>(config.apiUrl, walletAddress);\n if (current) {\n response = current;\n tokenAccountAddress = current.user.tokenAccountAddress ?? undefined;\n } else if (metadata.publicKeyX && metadata.publicKeyY) {\n response = await postJson<PasskeyAuthApiResponse<TExtra>>(\n `${config.apiUrl}/auth/register-passkey-wallet`,\n {\n pubkeyX: metadata.publicKeyX,\n pubkeyY: metadata.publicKeyY,\n credentialId: metadata.credentialId,\n }\n );\n walletAddress = response.user.publicKey;\n userId = response.user.id;\n tokenAccountAddress = response.user.tokenAccountAddress ?? undefined;\n await storeWalletInfo(walletAddress, userId, tokenAccountAddress);\n } else {\n await clearSession();\n await clearPasskeyMetadata();\n set({ isLoading: false, hasExistingPasskey: false, error: null });\n return false;\n }\n } else if (metadata.publicKeyX && metadata.publicKeyY) {\n response = await postJson<PasskeyAuthApiResponse<TExtra>>(\n `${config.apiUrl}/auth/register-passkey-wallet`,\n {\n pubkeyX: metadata.publicKeyX,\n pubkeyY: metadata.publicKeyY,\n credentialId: metadata.credentialId,\n }\n );\n walletAddress = response.user.publicKey;\n userId = response.user.id;\n tokenAccountAddress = response.user.tokenAccountAddress ?? undefined;\n await storeWalletInfo(walletAddress, userId, tokenAccountAddress);\n } else {\n const recovered = await postJson<PasskeyAuthApiResponse<TExtra>>(\n `${config.apiUrl}/auth/recover-passkey-wallet`,\n { credentialId: metadata.credentialId }\n ).catch(() => null);\n\n if (!recovered) {\n await clearSession();\n await clearPasskeyMetadata();\n set({ isLoading: false, hasExistingPasskey: false, error: null });\n return false;\n }\n\n response = recovered;\n walletAddress = recovered.user.publicKey;\n userId = recovered.user.id;\n tokenAccountAddress = recovered.user.tokenAccountAddress ?? undefined;\n await storeWalletInfo(walletAddress, userId, tokenAccountAddress);\n }\n\n set({\n isLoading: false,\n isAuthenticated: true,\n activeCredentialId: metadata.credentialId,\n address: walletAddress,\n user: response ? toPasskeyUser(response.user) : get().user,\n });\n\n return true;\n } catch (error) {\n console.error('Failed to unlock with passkey:', error);\n const kind = classifyPasskeyError(error);\n\n if (kind === 'USER_CANCELLED') {\n set({ isLoading: false });\n } else if (kind === 'NOT_FOUND') {\n await clearPasskeyMetadata();\n set({ isLoading: false, hasExistingPasskey: false, error: null });\n } else {\n set({\n isLoading: false,\n error: error instanceof Error ? error.message : 'Failed to unlock',\n });\n }\n\n return false;\n }\n },\n\n recoverWithDiscoverablePasskey: async () => {\n set({ isLoading: true, error: null, needsNewPasskey: false });\n\n try {\n const discovered = await authenticateWithDiscoverablePasskey({\n rpId: config.rpId,\n });\n\n if (!discovered) {\n set({ isLoading: false });\n return false;\n }\n\n const response = await postJson<PasskeyAuthApiResponse<TExtra>>(\n `${config.apiUrl}/auth/recover-passkey-wallet`,\n { credentialId: discovered.credentialId }\n ).catch(() => null);\n\n if (!response) {\n set({ isLoading: false, needsNewPasskey: true });\n return false;\n }\n\n const walletAddress = response.user.publicKey;\n const userId = response.user.id;\n const tokenAccountAddress = response.user.tokenAccountAddress ?? undefined;\n const now = new Date().toISOString();\n\n await storePasskeyMetadata({\n credentialId: discovered.credentialId,\n publicKeyX: '',\n publicKeyY: '',\n rpId: discovered.rpId,\n createdAt: now,\n lastUsedAt: now,\n });\n await storeWalletInfo(walletAddress, userId, tokenAccountAddress);\n\n set({\n isLoading: false,\n isAuthenticated: true,\n hasExistingPasskey: true,\n activeCredentialId: discovered.credentialId,\n address: walletAddress,\n user: toPasskeyUser(response.user),\n });\n\n return true;\n } catch (error) {\n console.error('Failed to recover with discoverable passkey:', error);\n set({\n isLoading: false,\n error:\n error instanceof Error ? error.message : 'Failed to recover wallet',\n });\n return false;\n }\n },\n\n logout: async () => {\n try {\n await clearSession();\n } catch (error) {\n console.error('Failed to clear passkey session:', error);\n }\n\n set({\n isAuthenticated: false,\n needsNewPasskey: false,\n user: null,\n address: null,\n activeCredentialId: null,\n });\n },\n\n clearError: () => set({ error: null }),\n dismissNewPasskey: () => set({ needsNewPasskey: false }),\n }));\n}\n\nexport function getPasskeyAuthStore<TExtra = Record<string, never>>(\n config: PasskeyAuthConfig\n): PasskeyAuthBoundStore<TExtra> {\n const key = createStoreKey(config);\n const cached = storeCache.get(key) as PasskeyAuthBoundStore<TExtra> | undefined;\n if (cached) return cached;\n\n const store = createPasskeyAuthStore<TExtra>(config);\n storeCache.set(key, store);\n return store;\n}\n\nexport function usePasskeyAuth<TExtra = Record<string, never>>(\n config: PasskeyAuthConfig\n): PasskeyAuthStore<TExtra> {\n return getPasskeyAuthStore<TExtra>(config)();\n}\n","const PASSKEY_ERRORS = {\n USER_CANCELLED: [\n 'error 1001',\n 'UserCancelled',\n 'Passkey authentication was cancelled',\n 'Passkey registration was cancelled',\n ],\n NOT_FOUND: [\n 'not found',\n 'No credentials available',\n 'no passkey',\n 'NoCredentials',\n ],\n} as const;\n\nexport type PasskeyErrorKind = keyof typeof PASSKEY_ERRORS;\n\nexport function classifyPasskeyError(error: unknown): PasskeyErrorKind | null {\n const message =\n error instanceof Error ? error.message : typeof error === 'string' ? error : null;\n\n if (!message) return null;\n\n for (const [kind, patterns] of Object.entries(PASSKEY_ERRORS)) {\n if (patterns.some((pattern) => message.includes(pattern))) {\n return kind as PasskeyErrorKind;\n }\n }\n\n return null;\n}\n"],"mappings":"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","names":["import_passkey_manager","passkeyCreate","passkeyGet","import_passkey_manager","ctx","import_passkey_manager","readJson"]}
|
|
1
|
+
{"version":3,"sources":["../src/auth/index.ts","../src/auth/execute-tx.ts","../src/mobile/passkey.ts","../src/mobile/storage.ts","../src/auth/add-device.ts","../src/auth/use-passkey-auth.ts","../src/mobile/errors.ts"],"sourcesContent":["export type {\n PasskeyApiUser,\n PasskeyAuthApiResponse,\n PasskeyUser,\n PasskeyAuthConfig,\n PasskeyAuthState,\n PasskeyAuthActions,\n PasskeyAuthStore,\n PasskeyAuthBoundStore,\n} from './types';\n\nexport { executePasskeyTransaction } from './execute-tx';\n\nexport { addDeviceToAccount } from './add-device';\nexport type {\n AddDeviceParams,\n AddDeviceResult,\n AnyThruClient,\n PasskeyChallengeSigner,\n TxExecutor,\n TxExecutorParams,\n TxExecutorResult,\n} from './add-device';\n\nexport {\n createPasskeyAuthStore,\n getPasskeyAuthStore,\n usePasskeyAuth,\n} from './use-passkey-auth';\n","import { base64UrlToBytes, bytesToBase64, bytesToHex } from '@thru/programs/passkey-manager';\nimport { signWithPasskey } from '../mobile/passkey';\nimport { touchPasskeyLastUsedAt } from '../mobile/storage';\n\nasync function readJson(response: Response): Promise<Record<string, unknown>> {\n try {\n return (await response.json()) as Record<string, unknown>;\n } catch {\n throw new Error(`Non-JSON response (HTTP ${response.status})`);\n }\n}\n\nexport async function executePasskeyTransaction<\n P extends Record<string, unknown>,\n R\n>(opts: {\n challengeUrl: string;\n submitUrl: string;\n params: P;\n credentialId: string;\n rpId: string;\n}): Promise<R> {\n let challengeRes: Response;\n try {\n challengeRes = await fetch(opts.challengeUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(opts.params),\n });\n } catch {\n throw new Error('Network request failed (challenge)');\n }\n\n const challengeData = await readJson(challengeRes);\n if (!challengeRes.ok || challengeData.success !== true) {\n throw new Error(\n typeof challengeData.error === 'string'\n ? challengeData.error\n : 'Failed to get challenge'\n );\n }\n\n if (typeof challengeData.challenge !== 'string') {\n throw new Error('Challenge response did not include a challenge');\n }\n\n const challengeBytes = base64UrlToBytes(challengeData.challenge);\n const signature = await signWithPasskey(\n opts.credentialId,\n challengeBytes,\n opts.rpId\n );\n await touchPasskeyLastUsedAt().catch((error) => {\n console.warn('Failed to update passkey last-used timestamp:', error);\n });\n\n const { success: _success, error: _error, ...challengeFields } = challengeData;\n\n let submitRes: Response;\n try {\n submitRes = await fetch(opts.submitUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...opts.params,\n ...challengeFields,\n signatureR: bytesToHex(signature.signatureR),\n signatureS: bytesToHex(signature.signatureS),\n authenticatorData: bytesToBase64(signature.authenticatorData),\n clientDataJSON: bytesToBase64(signature.clientDataJSON),\n }),\n });\n } catch {\n throw new Error('Network request failed (submit)');\n }\n\n const submitData = await readJson(submitRes);\n if (!submitRes.ok || submitData.success !== true) {\n throw new Error(\n typeof submitData.error === 'string'\n ? submitData.error\n : 'Failed to submit transaction'\n );\n }\n\n return submitData as R;\n}\n","import { create as passkeyCreate, get as passkeyGet } from 'react-native-passkeys';\nimport {\n bytesToBase64Url,\n base64UrlToBytes,\n normalizeLowS,\n parseDerSignature,\n type PasskeySigningResult,\n} from '@thru/programs/passkey-manager';\nimport type {\n DiscoverablePasskeyResult,\n PasskeyMobileConfig,\n PasskeyRegistrationResult,\n} from './types';\n\ntype ProcessLike = typeof globalThis & {\n process?: {\n env?: Record<string, string | undefined>;\n };\n};\n\nfunction getDefaultConfig(config?: PasskeyMobileConfig): Required<PasskeyMobileConfig> {\n const env = (globalThis as ProcessLike).process?.env ?? {};\n\n return {\n rpId: config?.rpId ?? env.EXPO_PUBLIC_PASSKEY_RP_ID ?? 'wallet.thru.org',\n rpName: config?.rpName ?? env.EXPO_PUBLIC_PASSKEY_RP_NAME ?? 'Thru Wallet',\n };\n}\n\nexport async function registerPasskey(\n alias: string,\n userId: string,\n config?: PasskeyMobileConfig\n): Promise<PasskeyRegistrationResult> {\n const { rpId, rpName } = getDefaultConfig(config);\n const challenge = bytesToBase64Url(crypto.getRandomValues(new Uint8Array(32)));\n const userIdB64 = bytesToBase64Url(new TextEncoder().encode(userId));\n\n const result = await passkeyCreate({\n challenge,\n rp: { id: rpId, name: rpName },\n user: { id: userIdB64, name: alias, displayName: alias },\n pubKeyCredParams: [{ type: 'public-key', alg: -7 }],\n authenticatorSelection: {\n authenticatorAttachment: 'platform',\n userVerification: 'required',\n residentKey: 'required',\n },\n attestation: 'none',\n timeout: 60000,\n });\n\n if (!result) {\n throw new Error('Passkey registration was cancelled');\n }\n\n const publicKeyB64 = result.response.getPublicKey?.();\n if (!publicKeyB64) {\n throw new Error('Failed to retrieve public key from registration');\n }\n\n const keyBytes = base64UrlToBytes(publicKeyB64);\n const { x, y } = extractP256Coordinates(keyBytes);\n\n return {\n credentialId: result.id,\n publicKeyX: x,\n publicKeyY: y,\n rpId,\n };\n}\n\nexport async function signWithPasskey(\n credentialId: string,\n challenge: Uint8Array,\n rpId?: string\n): Promise<PasskeySigningResult> {\n const resolvedRpId = rpId ?? getDefaultConfig().rpId;\n const challengeB64 = bytesToBase64Url(challenge);\n\n const result = await passkeyGet({\n challenge: challengeB64,\n rpId: resolvedRpId,\n allowCredentials: [{ type: 'public-key', id: credentialId }],\n userVerification: 'required',\n timeout: 60000,\n });\n\n if (!result) {\n throw new Error('Passkey authentication was cancelled');\n }\n\n const derSignature = base64UrlToBytes(result.response.signature);\n let { r, s } = parseDerSignature(derSignature);\n s = normalizeLowS(s);\n\n return {\n signature: new Uint8Array([...r, ...s]),\n authenticatorData: base64UrlToBytes(result.response.authenticatorData),\n clientDataJSON: base64UrlToBytes(result.response.clientDataJSON),\n signatureR: r,\n signatureS: s,\n };\n}\n\nexport async function authenticateWithDiscoverablePasskey(\n config?: Pick<PasskeyMobileConfig, 'rpId'>\n): Promise<DiscoverablePasskeyResult | null> {\n const { rpId } = getDefaultConfig(config);\n\n try {\n const challenge = bytesToBase64Url(crypto.getRandomValues(new Uint8Array(32)));\n const result = await passkeyGet({\n challenge,\n rpId,\n userVerification: 'required',\n timeout: 60000,\n });\n\n return result ? { credentialId: result.id, rpId } : null;\n } catch {\n return null;\n }\n}\n\nexport function extractP256Coordinates(\n keyBytes: Uint8Array\n): { x: Uint8Array; y: Uint8Array } {\n if (keyBytes.length === 64) {\n return {\n x: keyBytes.slice(0, 32),\n y: keyBytes.slice(32, 64),\n };\n }\n\n if (keyBytes.length === 65 && keyBytes[0] === 0x04) {\n return {\n x: keyBytes.slice(1, 33),\n y: keyBytes.slice(33, 65),\n };\n }\n\n const pointStart = keyBytes.length - 65;\n if (pointStart > 0 && keyBytes[pointStart] === 0x04) {\n return {\n x: keyBytes.slice(pointStart + 1, pointStart + 33),\n y: keyBytes.slice(pointStart + 33, pointStart + 65),\n };\n }\n\n throw new Error(\n `Unsupported public key format (${keyBytes.length} bytes). Expected raw X||Y (64), uncompressed point (65), or SPKI DER (91).`\n );\n}\n","import * as SecureStore from 'expo-secure-store';\nimport type { PasskeyMetadata } from '@thru/programs/passkey-manager';\n\nconst SECURE_STORE_OPTS = {\n keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,\n} as const;\n\nconst PASSKEY_CREDENTIAL_ID_KEY = 'thru_passkey_credential_id';\nconst PASSKEY_PUBLIC_KEY_X_KEY = 'thru_passkey_pubkey_x';\nconst PASSKEY_PUBLIC_KEY_Y_KEY = 'thru_passkey_pubkey_y';\nconst PASSKEY_RP_ID_KEY = 'thru_passkey_rp_id';\nconst PASSKEY_LABEL_KEY = 'thru_passkey_label';\nconst PASSKEY_CREATED_AT_KEY = 'thru_passkey_created_at';\nconst PASSKEY_LAST_USED_AT_KEY = 'thru_passkey_last_used_at';\n\nconst ADDRESS_KEY = 'thru_address';\nconst USER_ID_KEY = 'thru_user_id';\nconst TOKEN_ACCOUNT_KEY = 'thru_token_account';\n\nexport async function storePasskeyMetadata(metadata: PasskeyMetadata): Promise<void> {\n await Promise.all([\n SecureStore.setItemAsync(PASSKEY_CREDENTIAL_ID_KEY, metadata.credentialId, SECURE_STORE_OPTS),\n SecureStore.setItemAsync(PASSKEY_PUBLIC_KEY_X_KEY, metadata.publicKeyX, SECURE_STORE_OPTS),\n SecureStore.setItemAsync(PASSKEY_PUBLIC_KEY_Y_KEY, metadata.publicKeyY, SECURE_STORE_OPTS),\n SecureStore.setItemAsync(PASSKEY_RP_ID_KEY, metadata.rpId, SECURE_STORE_OPTS),\n SecureStore.setItemAsync(PASSKEY_LABEL_KEY, metadata.label ?? '', SECURE_STORE_OPTS),\n SecureStore.setItemAsync(PASSKEY_CREATED_AT_KEY, metadata.createdAt, SECURE_STORE_OPTS),\n SecureStore.setItemAsync(PASSKEY_LAST_USED_AT_KEY, metadata.lastUsedAt, SECURE_STORE_OPTS),\n ]);\n}\n\nexport async function getStoredPasskeyMetadata(): Promise<PasskeyMetadata | null> {\n const credentialId = await SecureStore.getItemAsync(PASSKEY_CREDENTIAL_ID_KEY);\n if (!credentialId) return null;\n\n const [publicKeyX, publicKeyY, rpId, label, createdAt, lastUsedAt] = await Promise.all([\n SecureStore.getItemAsync(PASSKEY_PUBLIC_KEY_X_KEY),\n SecureStore.getItemAsync(PASSKEY_PUBLIC_KEY_Y_KEY),\n SecureStore.getItemAsync(PASSKEY_RP_ID_KEY),\n SecureStore.getItemAsync(PASSKEY_LABEL_KEY),\n SecureStore.getItemAsync(PASSKEY_CREATED_AT_KEY),\n SecureStore.getItemAsync(PASSKEY_LAST_USED_AT_KEY),\n ]);\n\n if (!rpId || !createdAt) return null;\n\n return {\n credentialId,\n publicKeyX: publicKeyX ?? '',\n publicKeyY: publicKeyY ?? '',\n rpId,\n label: label || undefined,\n createdAt,\n lastUsedAt: lastUsedAt ?? createdAt,\n };\n}\n\nexport async function touchPasskeyLastUsedAt(lastUsedAt = new Date().toISOString()): Promise<string> {\n await SecureStore.setItemAsync(PASSKEY_LAST_USED_AT_KEY, lastUsedAt, SECURE_STORE_OPTS);\n return lastUsedAt;\n}\n\nexport async function hasStoredPasskey(): Promise<boolean> {\n return (await SecureStore.getItemAsync(PASSKEY_CREDENTIAL_ID_KEY)) !== null;\n}\n\nexport async function clearPasskeyMetadata(): Promise<void> {\n await Promise.all([\n SecureStore.deleteItemAsync(PASSKEY_CREDENTIAL_ID_KEY),\n SecureStore.deleteItemAsync(PASSKEY_PUBLIC_KEY_X_KEY),\n SecureStore.deleteItemAsync(PASSKEY_PUBLIC_KEY_Y_KEY),\n SecureStore.deleteItemAsync(PASSKEY_RP_ID_KEY),\n SecureStore.deleteItemAsync(PASSKEY_LABEL_KEY),\n SecureStore.deleteItemAsync(PASSKEY_CREATED_AT_KEY),\n SecureStore.deleteItemAsync(PASSKEY_LAST_USED_AT_KEY),\n ]);\n}\n\nexport async function storeWalletInfo(\n address: string,\n userId: string,\n tokenAccountAddress?: string\n): Promise<void> {\n await Promise.all([\n SecureStore.setItemAsync(ADDRESS_KEY, address, SECURE_STORE_OPTS),\n SecureStore.setItemAsync(USER_ID_KEY, userId, SECURE_STORE_OPTS),\n tokenAccountAddress\n ? SecureStore.setItemAsync(TOKEN_ACCOUNT_KEY, tokenAccountAddress, SECURE_STORE_OPTS)\n : SecureStore.deleteItemAsync(TOKEN_ACCOUNT_KEY),\n ]);\n}\n\nexport async function hasStoredWallet(): Promise<boolean> {\n return (await SecureStore.getItemAsync(ADDRESS_KEY)) !== null;\n}\n\nexport async function getStoredAddress(): Promise<string | null> {\n return SecureStore.getItemAsync(ADDRESS_KEY);\n}\n\nexport async function getStoredUserId(): Promise<string | null> {\n return SecureStore.getItemAsync(USER_ID_KEY);\n}\n\nexport async function getStoredTokenAccount(): Promise<string | null> {\n return SecureStore.getItemAsync(TOKEN_ACCOUNT_KEY);\n}\n\nexport async function clearSession(): Promise<void> {\n await Promise.all([\n SecureStore.deleteItemAsync(ADDRESS_KEY),\n SecureStore.deleteItemAsync(USER_ID_KEY),\n SecureStore.deleteItemAsync(TOKEN_ACCOUNT_KEY),\n ]);\n}\n","/* Add-passkey-to-account transaction builder, lifted from\n `web/wallet-auth-manager/app/page.tsx` (`runValidateThen`,\n `handleSubmitAddPasskey`) so the wallet's `/embedded` post-connect\n step can reuse the exact same flow.\n\n Builds the on-chain transaction:\n VALIDATE(existingAuthority -> ADD_AUTHORITY(newPasskey))\n or VALIDATE(existingAuthority -> multicall[ADD_AUTHORITY, REGISTER_CREDENTIAL])\n asks the caller's existing passkey to sign the challenge, then asks\n the caller's wallet signer to sign the assembled transaction, sends\n it, and returns the result. */\n\nimport {\n type Authority,\n buildAccountContext,\n createValidateChallenge,\n createCredentialLookupSeed,\n decodeAddress,\n deriveWalletAddress,\n encodeAddAuthorityInstruction,\n encodeRegisterCredentialInstruction,\n encodeValidateInstruction,\n parseWalletAuthorities,\n type ParsedAuthority,\n type WalletSigner,\n} from \"@thru/programs/passkey-manager\";\nimport {\n MULTICALL_PROGRAM_PUBKEY,\n buildMulticallInstruction,\n} from \"@thru/programs/multicall\";\n\n/** Minimal shape required from a passkey signer. Both web's\n `signWithDiscoverablePasskey`/`signWithPasskey` and mobile's\n counterparts conform. */\nexport interface PasskeyChallengeSigner {\n signChallenge: (challenge: Uint8Array) => Promise<{\n signatureR: Uint8Array;\n signatureS: Uint8Array;\n authenticatorData: Uint8Array;\n clientDataJSON: Uint8Array;\n }>;\n}\n\n/** Minimal shape required from a Thru chain client. Loosely-typed\n because @thru/sdk's DTS emit is currently broken in this\n repo. The caller passes the real Thru and we narrow operationally. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyThruClient = any;\n\nexport interface AddDeviceParams {\n /** Loosely-typed Thru chain client (`@thru/sdk/client`). */\n thru: AnyThruClient;\n /** Wallet (the on-chain WalletAccount) to attach the passkey to. */\n walletAddress: string;\n /** Index of the existing authority that approves this change. Must\n currently be a passkey authority. */\n authIdx: number;\n /** New passkey to attach. tag = 1 (passkey). */\n newAuthority: Authority;\n /** Optional credential-lookup registration so the new passkey is\n discoverable on subsequent sign-ins. */\n credentialId?: Uint8Array;\n walletName?: string;\n /** Existing-passkey challenge signer (web or mobile). */\n passkey: PasskeyChallengeSigner;\n /** Wallet transaction signer that returns base64(signed bytes). */\n walletSigner: WalletSigner;\n /** Passkey program address (base58). */\n programAddress: string;\n /** Sign-and-send executor (lifted from passkey-transaction.ts in the\n wallet-auth-manager - wallet apps own this because it depends on\n the `Thru` client's transaction builder). */\n executor: TxExecutor;\n /** Optional status callback so UIs can show progress. */\n onStatus?: (message: string) => void;\n}\n\nexport interface TxExecutorParams {\n thru: AnyThruClient;\n walletSigner: WalletSigner;\n instructionData: Uint8Array;\n readWriteAddresses: string[];\n readOnlyAddresses: string[];\n label: string;\n}\n\nexport interface TxExecutorResult {\n signature: string;\n /** Loosely-typed because @thru/sdk types aren't available. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n execution: any;\n}\n\nexport type TxExecutor = (\n params: TxExecutorParams,\n) => Promise<TxExecutorResult>;\n\nexport interface AddDeviceResult extends TxExecutorResult {\n /** The new passkey's authority index after the transaction lands. */\n newAuthorityIdx: number;\n}\n\n/**\n * Run VALIDATE + ADD_AUTHORITY [+ REGISTER_CREDENTIAL] to attach a new\n * passkey to an on-chain WalletAccount.\n */\nexport async function addDeviceToAccount(\n params: AddDeviceParams,\n): Promise<AddDeviceResult> {\n const status = params.onStatus ?? (() => {});\n\n const walletAccount = await params.thru.accounts.get(params.walletAddress);\n const walletData: Uint8Array | undefined = walletAccount?.data?.data;\n if (!walletData) {\n throw new Error(\"Wallet account data missing\");\n }\n\n const parsed = parseWalletAuthorities(walletData);\n const authorizing: ParsedAuthority | undefined =\n parsed.authorities[params.authIdx];\n if (!authorizing) {\n throw new Error(\"Authorization index out of bounds\");\n }\n if (authorizing.kind !== \"passkey\") {\n throw new Error(\n \"addDeviceToAccount currently requires a passkey authority for VALIDATE\",\n );\n }\n\n /* The new authority will land at the next free slot. */\n const newAuthorityIdx = parsed.authorities.length;\n\n let readWriteAccounts: Uint8Array[] = [];\n let lookupSeed: Uint8Array | undefined;\n let lookupAddressBytes: Uint8Array | undefined;\n let lookupProof: Uint8Array | undefined;\n\n if (params.credentialId) {\n lookupSeed = await createCredentialLookupSeed(params.credentialId);\n lookupAddressBytes = await deriveWalletAddress(\n lookupSeed,\n params.programAddress,\n );\n\n status(\"Fetching state proof for credential lookup...\");\n const proofResult = await params.thru.proofs.generate({\n proofType: 1 /* StateProofType.CREATING */,\n address: lookupAddressBytes,\n });\n lookupProof = proofResult.proof;\n readWriteAccounts = [lookupAddressBytes];\n }\n\n const ctx = buildAccountContext({\n walletAddress: params.walletAddress,\n readWriteAccounts,\n readOnlyAccounts: params.credentialId ? [MULTICALL_PROGRAM_PUBKEY] : [],\n programAddress: params.programAddress,\n });\n\n const passkeyProgramPubkey = decodeAddress(params.programAddress);\n const addAuthorityInstruction = encodeAddAuthorityInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n authority: params.newAuthority,\n });\n\n let targetProgramIdx = ctx.getAccountIndex(passkeyProgramPubkey);\n let targetInstructionData = addAuthorityInstruction;\n\n if (params.credentialId) {\n if (!lookupSeed || !lookupAddressBytes || !lookupProof) {\n throw new Error(\"Credential lookup proof data missing\");\n }\n\n const registerCredentialInstruction = encodeRegisterCredentialInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n lookupAccountIdx: ctx.getAccountIndex(lookupAddressBytes),\n seed: lookupSeed,\n stateProof: lookupProof,\n });\n\n targetProgramIdx = ctx.getAccountIndex(MULTICALL_PROGRAM_PUBKEY);\n targetInstructionData = buildMulticallInstruction([\n {\n programIdx: ctx.getAccountIndex(passkeyProgramPubkey),\n instructionData: addAuthorityInstruction,\n },\n {\n programIdx: ctx.getAccountIndex(passkeyProgramPubkey),\n instructionData: registerCredentialInstruction,\n },\n ]);\n }\n\n /* Build the VALIDATE challenge over the target CPI and ask the caller's passkey to sign. */\n const challenge = await createValidateChallenge(\n parsed.nonce,\n ctx.accountAddresses,\n ctx.walletAccountIdx,\n params.authIdx,\n {\n programIdx: targetProgramIdx,\n instructionData: targetInstructionData,\n },\n );\n\n status(\"Waiting for passkey approval...\");\n const signature = await params.passkey.signChallenge(challenge);\n\n const validateInstruction = encodeValidateInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n authIdx: params.authIdx,\n targetInstruction: {\n programIdx: targetProgramIdx,\n instructionData: targetInstructionData,\n },\n signatureR: signature.signatureR,\n signatureS: signature.signatureS,\n authenticatorData: signature.authenticatorData,\n clientDataJSON: signature.clientDataJSON,\n });\n\n status(\"Sending transaction...\");\n const result = await params.executor({\n thru: params.thru,\n walletSigner: params.walletSigner,\n instructionData: validateInstruction,\n readWriteAddresses: ctx.readWriteAddresses,\n readOnlyAddresses: ctx.readOnlyAddresses,\n label: params.credentialId\n ? \"VALIDATE -> MULTICALL(ADD_AUTHORITY, REGISTER_CREDENTIAL)\"\n : \"VALIDATE -> ADD_AUTHORITY\",\n });\n\n return { ...result, newAuthorityIdx };\n}\n","import { bytesToHex } from '@thru/programs/passkey-manager';\nimport { create } from 'zustand';\nimport { classifyPasskeyError } from '../mobile/errors';\nimport {\n authenticateWithDiscoverablePasskey,\n registerPasskey,\n signWithPasskey,\n} from '../mobile/passkey';\nimport {\n clearPasskeyMetadata,\n clearSession,\n getStoredAddress,\n getStoredPasskeyMetadata,\n getStoredUserId,\n hasStoredPasskey,\n hasStoredWallet,\n storePasskeyMetadata,\n storeWalletInfo,\n touchPasskeyLastUsedAt,\n} from '../mobile/storage';\nimport type {\n PasskeyAuthApiResponse,\n PasskeyAuthBoundStore,\n PasskeyAuthConfig,\n PasskeyAuthStore,\n PasskeyUser,\n} from './types';\n\nconst storeCache = new Map<string, PasskeyAuthBoundStore<any>>();\n\nfunction createStoreKey(config: PasskeyAuthConfig): string {\n return [config.apiUrl, config.alias ?? '', config.rpId ?? '', config.rpName ?? ''].join('::');\n}\n\nfunction buildDisplayName(address: string): string {\n return `${address.slice(0, 8)}...${address.slice(-4)}`;\n}\n\nfunction toPasskeyUser<TExtra>(\n user: PasskeyAuthApiResponse<TExtra>['user']\n): PasskeyUser<TExtra> {\n return {\n id: user.id,\n displayName: buildDisplayName(user.publicKey),\n tokenAccountAddress: user.tokenAccountAddress ?? null,\n extras: user.extras,\n };\n}\n\nasync function readJson<T>(response: Response): Promise<T> {\n return (await response.json()) as T;\n}\n\nasync function postJson<T>(\n url: string,\n body: Record<string, unknown>\n): Promise<T> {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n const data = await readJson<Record<string, unknown>>(response);\n if (!response.ok || data.success !== true) {\n throw new Error(\n typeof data.error === 'string' ? data.error : 'Request failed'\n );\n }\n\n return data as unknown as T;\n}\n\nasync function getCurrentUser<TExtra>(\n apiUrl: string,\n address: string\n): Promise<PasskeyAuthApiResponse<TExtra> | null> {\n const response = await fetch(`${apiUrl}/auth/me`, {\n headers: {\n 'Content-Type': 'application/json',\n 'x-wallet-address': address,\n },\n });\n\n if (response.status === 404) return null;\n\n const data = await readJson<Record<string, unknown>>(response);\n if (!response.ok || data.success !== true) {\n throw new Error(\n typeof data.error === 'string' ? data.error : 'Failed to fetch current user'\n );\n }\n\n return data as unknown as PasskeyAuthApiResponse<TExtra>;\n}\n\nexport function createPasskeyAuthStore<TExtra = Record<string, never>>(\n config: PasskeyAuthConfig\n): PasskeyAuthBoundStore<TExtra> {\n const resolvedAlias = config.alias ?? 'Thru Wallet';\n\n return create<PasskeyAuthStore<TExtra>>((set, get) => ({\n isAuthenticated: false,\n isLoading: false,\n isInitialized: false,\n hasExistingPasskey: false,\n needsNewPasskey: false,\n error: null,\n user: null,\n address: null,\n activeCredentialId: null,\n\n initialize: async () => {\n try {\n const hasPasskey = await hasStoredPasskey();\n\n if (hasPasskey) {\n const storedAddress = await getStoredAddress();\n if (storedAddress) {\n const user = await Promise.race([\n getCurrentUser<TExtra>(config.apiUrl, storedAddress),\n new Promise<null>((_, reject) =>\n setTimeout(() => reject(new Error('timeout')), 2000)\n ),\n ]).catch(() => undefined);\n\n if (user === null) {\n await clearSession();\n await clearPasskeyMetadata();\n set({ isInitialized: true, hasExistingPasskey: false });\n return;\n }\n } else {\n const metadata = await getStoredPasskeyMetadata();\n if (metadata && !metadata.publicKeyX && !metadata.publicKeyY) {\n await clearPasskeyMetadata();\n set({ isInitialized: true, hasExistingPasskey: false });\n return;\n }\n }\n }\n\n set({ isInitialized: true, hasExistingPasskey: hasPasskey });\n } catch (error) {\n console.error('Failed to initialize passkey auth:', error);\n set({ isInitialized: true, error: 'Failed to initialize wallet' });\n }\n },\n\n createWallet: async () => {\n set({ isLoading: true, error: null, needsNewPasskey: false });\n\n try {\n const tempId = `user-${Date.now()}`;\n const passkeyLabel = resolvedAlias.trim() || 'Thru Wallet';\n const { credentialId, publicKeyX, publicKeyY, rpId } =\n await registerPasskey(passkeyLabel, tempId, {\n rpId: config.rpId,\n rpName: config.rpName,\n });\n\n const now = new Date().toISOString();\n const pubkeyXHex = bytesToHex(publicKeyX);\n const pubkeyYHex = bytesToHex(publicKeyY);\n\n await storePasskeyMetadata({\n credentialId,\n publicKeyX: pubkeyXHex,\n publicKeyY: pubkeyYHex,\n rpId,\n label: passkeyLabel,\n createdAt: now,\n lastUsedAt: now,\n });\n\n const response = await postJson<PasskeyAuthApiResponse<TExtra>>(\n `${config.apiUrl}/auth/register-passkey-wallet`,\n {\n pubkeyX: pubkeyXHex,\n pubkeyY: pubkeyYHex,\n credentialId,\n }\n );\n\n const walletAddress = response.user.publicKey;\n await storeWalletInfo(\n walletAddress,\n response.user.id,\n response.user.tokenAccountAddress ?? undefined\n );\n\n set({\n isLoading: false,\n isAuthenticated: true,\n hasExistingPasskey: true,\n activeCredentialId: credentialId,\n address: walletAddress,\n user: toPasskeyUser(response.user),\n });\n\n return true;\n } catch (error) {\n if (classifyPasskeyError(error) === 'USER_CANCELLED') {\n set({ isLoading: false });\n return false;\n }\n\n console.error('Failed to create passkey wallet:', error);\n set({\n isLoading: false,\n error: error instanceof Error ? error.message : 'Failed to create wallet',\n });\n return false;\n }\n },\n\n unlockWithPasskey: async () => {\n set({ isLoading: true, error: null, needsNewPasskey: false });\n\n try {\n const metadata = await getStoredPasskeyMetadata();\n if (!metadata) throw new Error('No stored passkey found');\n\n await signWithPasskey(\n metadata.credentialId,\n crypto.getRandomValues(new Uint8Array(32)),\n metadata.rpId\n );\n await touchPasskeyLastUsedAt().catch((error) => {\n console.warn('Failed to update passkey last-used timestamp:', error);\n });\n\n const hasWallet = await hasStoredWallet();\n let walletAddress: string;\n let userId: string;\n let tokenAccountAddress: string | undefined;\n let response: PasskeyAuthApiResponse<TExtra> | null = null;\n\n if (hasWallet) {\n const storedAddress = await getStoredAddress();\n const storedUserId = await getStoredUserId();\n if (!storedAddress || !storedUserId) {\n throw new Error('Incomplete wallet data');\n }\n\n walletAddress = storedAddress;\n userId = storedUserId;\n\n const current = await getCurrentUser<TExtra>(config.apiUrl, walletAddress);\n if (current) {\n response = current;\n tokenAccountAddress = current.user.tokenAccountAddress ?? undefined;\n } else if (metadata.publicKeyX && metadata.publicKeyY) {\n response = await postJson<PasskeyAuthApiResponse<TExtra>>(\n `${config.apiUrl}/auth/register-passkey-wallet`,\n {\n pubkeyX: metadata.publicKeyX,\n pubkeyY: metadata.publicKeyY,\n credentialId: metadata.credentialId,\n }\n );\n walletAddress = response.user.publicKey;\n userId = response.user.id;\n tokenAccountAddress = response.user.tokenAccountAddress ?? undefined;\n await storeWalletInfo(walletAddress, userId, tokenAccountAddress);\n } else {\n await clearSession();\n await clearPasskeyMetadata();\n set({ isLoading: false, hasExistingPasskey: false, error: null });\n return false;\n }\n } else if (metadata.publicKeyX && metadata.publicKeyY) {\n response = await postJson<PasskeyAuthApiResponse<TExtra>>(\n `${config.apiUrl}/auth/register-passkey-wallet`,\n {\n pubkeyX: metadata.publicKeyX,\n pubkeyY: metadata.publicKeyY,\n credentialId: metadata.credentialId,\n }\n );\n walletAddress = response.user.publicKey;\n userId = response.user.id;\n tokenAccountAddress = response.user.tokenAccountAddress ?? undefined;\n await storeWalletInfo(walletAddress, userId, tokenAccountAddress);\n } else {\n const recovered = await postJson<PasskeyAuthApiResponse<TExtra>>(\n `${config.apiUrl}/auth/recover-passkey-wallet`,\n { credentialId: metadata.credentialId }\n ).catch(() => null);\n\n if (!recovered) {\n await clearSession();\n await clearPasskeyMetadata();\n set({ isLoading: false, hasExistingPasskey: false, error: null });\n return false;\n }\n\n response = recovered;\n walletAddress = recovered.user.publicKey;\n userId = recovered.user.id;\n tokenAccountAddress = recovered.user.tokenAccountAddress ?? undefined;\n await storeWalletInfo(walletAddress, userId, tokenAccountAddress);\n }\n\n set({\n isLoading: false,\n isAuthenticated: true,\n activeCredentialId: metadata.credentialId,\n address: walletAddress,\n user: response ? toPasskeyUser(response.user) : get().user,\n });\n\n return true;\n } catch (error) {\n console.error('Failed to unlock with passkey:', error);\n const kind = classifyPasskeyError(error);\n\n if (kind === 'USER_CANCELLED') {\n set({ isLoading: false });\n } else if (kind === 'NOT_FOUND') {\n await clearPasskeyMetadata();\n set({ isLoading: false, hasExistingPasskey: false, error: null });\n } else {\n set({\n isLoading: false,\n error: error instanceof Error ? error.message : 'Failed to unlock',\n });\n }\n\n return false;\n }\n },\n\n recoverWithDiscoverablePasskey: async () => {\n set({ isLoading: true, error: null, needsNewPasskey: false });\n\n try {\n const discovered = await authenticateWithDiscoverablePasskey({\n rpId: config.rpId,\n });\n\n if (!discovered) {\n set({ isLoading: false });\n return false;\n }\n\n const response = await postJson<PasskeyAuthApiResponse<TExtra>>(\n `${config.apiUrl}/auth/recover-passkey-wallet`,\n { credentialId: discovered.credentialId }\n ).catch(() => null);\n\n if (!response) {\n set({ isLoading: false, needsNewPasskey: true });\n return false;\n }\n\n const walletAddress = response.user.publicKey;\n const userId = response.user.id;\n const tokenAccountAddress = response.user.tokenAccountAddress ?? undefined;\n const now = new Date().toISOString();\n\n await storePasskeyMetadata({\n credentialId: discovered.credentialId,\n publicKeyX: '',\n publicKeyY: '',\n rpId: discovered.rpId,\n createdAt: now,\n lastUsedAt: now,\n });\n await storeWalletInfo(walletAddress, userId, tokenAccountAddress);\n\n set({\n isLoading: false,\n isAuthenticated: true,\n hasExistingPasskey: true,\n activeCredentialId: discovered.credentialId,\n address: walletAddress,\n user: toPasskeyUser(response.user),\n });\n\n return true;\n } catch (error) {\n console.error('Failed to recover with discoverable passkey:', error);\n set({\n isLoading: false,\n error:\n error instanceof Error ? error.message : 'Failed to recover wallet',\n });\n return false;\n }\n },\n\n logout: async () => {\n try {\n await clearSession();\n } catch (error) {\n console.error('Failed to clear passkey session:', error);\n }\n\n set({\n isAuthenticated: false,\n needsNewPasskey: false,\n user: null,\n address: null,\n activeCredentialId: null,\n });\n },\n\n clearError: () => set({ error: null }),\n dismissNewPasskey: () => set({ needsNewPasskey: false }),\n }));\n}\n\nexport function getPasskeyAuthStore<TExtra = Record<string, never>>(\n config: PasskeyAuthConfig\n): PasskeyAuthBoundStore<TExtra> {\n const key = createStoreKey(config);\n const cached = storeCache.get(key) as PasskeyAuthBoundStore<TExtra> | undefined;\n if (cached) return cached;\n\n const store = createPasskeyAuthStore<TExtra>(config);\n storeCache.set(key, store);\n return store;\n}\n\nexport function usePasskeyAuth<TExtra = Record<string, never>>(\n config: PasskeyAuthConfig\n): PasskeyAuthStore<TExtra> {\n return getPasskeyAuthStore<TExtra>(config)();\n}\n","const PASSKEY_ERRORS = {\n USER_CANCELLED: [\n 'error 1001',\n 'UserCancelled',\n 'Passkey authentication was cancelled',\n 'Passkey registration was cancelled',\n ],\n NOT_FOUND: [\n 'not found',\n 'No credentials available',\n 'no passkey',\n 'NoCredentials',\n ],\n} as const;\n\nexport type PasskeyErrorKind = keyof typeof PASSKEY_ERRORS;\n\nexport function classifyPasskeyError(error: unknown): PasskeyErrorKind | null {\n const message =\n error instanceof Error ? error.message : typeof error === 'string' ? error : null;\n\n if (!message) return null;\n\n for (const [kind, patterns] of Object.entries(PASSKEY_ERRORS)) {\n if (patterns.some((pattern) => message.includes(pattern))) {\n return kind as PasskeyErrorKind;\n }\n }\n\n return null;\n}\n"],"mappings":"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","names":["import_passkey_manager","passkeyCreate","passkeyGet","import_passkey_manager","import_passkey_manager","readJson"]}
|
package/dist/auth.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "./chunk-ZNBMADOM.js";
|
|
17
17
|
import {
|
|
18
18
|
addDeviceToAccount
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-KASTJBBY.js";
|
|
20
20
|
|
|
21
21
|
// src/auth/execute-tx.ts
|
|
22
22
|
import { base64UrlToBytes, bytesToBase64, bytesToHex } from "@thru/programs/passkey-manager";
|