@thru/passkey 0.2.27 → 0.2.29

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.
Files changed (50) hide show
  1. package/dist/auth/add-device.cjs +11 -2
  2. package/dist/auth/add-device.cjs.map +1 -1
  3. package/dist/auth/add-device.d.cts +12 -2
  4. package/dist/auth/add-device.d.ts +12 -2
  5. package/dist/auth/add-device.js +3 -1
  6. package/dist/auth.cjs +12 -3
  7. package/dist/auth.cjs.map +1 -1
  8. package/dist/auth.d.cts +1 -1
  9. package/dist/auth.d.ts +1 -1
  10. package/dist/auth.js +4 -2
  11. package/dist/auth.js.map +1 -1
  12. package/dist/{chunk-ZNBMADOM.js → chunk-L53S4RKQ.js} +2 -2
  13. package/dist/chunk-L53S4RKQ.js.map +1 -0
  14. package/dist/{chunk-KASTJBBY.js → chunk-NFK5D7EI.js} +15 -5
  15. package/dist/chunk-NFK5D7EI.js.map +1 -0
  16. package/dist/{chunk-TW7HANJM.js → chunk-YV4LITL4.js} +40 -5
  17. package/dist/chunk-YV4LITL4.js.map +1 -0
  18. package/dist/index.cjs +39 -4
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +1 -1
  23. package/dist/mobile.cjs +1 -1
  24. package/dist/mobile.cjs.map +1 -1
  25. package/dist/mobile.js +1 -1
  26. package/dist/popup.d.cts +2 -2
  27. package/dist/popup.d.ts +2 -2
  28. package/dist/server.cjs +2 -2
  29. package/dist/server.cjs.map +1 -1
  30. package/dist/server.js +3 -2
  31. package/dist/server.js.map +1 -1
  32. package/dist/{types-BTTlCVrw.d.cts → types-CIzH-qtR.d.cts} +2 -0
  33. package/dist/{types-BTTlCVrw.d.ts → types-CIzH-qtR.d.ts} +2 -0
  34. package/dist/web.cjs +39 -4
  35. package/dist/web.cjs.map +1 -1
  36. package/dist/web.d.cts +2 -2
  37. package/dist/web.d.ts +2 -2
  38. package/dist/web.js +1 -1
  39. package/package.json +3 -3
  40. package/src/auth/add-device.ts +31 -3
  41. package/src/auth/index.ts +2 -1
  42. package/src/mobile/passkey.ts +1 -1
  43. package/src/server/create-wallet.test.ts +4 -0
  44. package/src/server/create-wallet.ts +3 -2
  45. package/src/sign.test.ts +48 -0
  46. package/src/sign.ts +52 -4
  47. package/src/types.ts +2 -0
  48. package/dist/chunk-KASTJBBY.js.map +0 -1
  49. package/dist/chunk-TW7HANJM.js.map +0 -1
  50. package/dist/chunk-ZNBMADOM.js.map +0 -1
@@ -20,12 +20,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/auth/add-device.ts
21
21
  var add_device_exports = {};
22
22
  __export(add_device_exports, {
23
+ addAuthorityToAccount: () => addAuthorityToAccount,
23
24
  addDeviceToAccount: () => addDeviceToAccount
24
25
  });
25
26
  module.exports = __toCommonJS(add_device_exports);
26
27
  var import_passkey_manager = require("@thru/programs/passkey-manager");
27
28
  var import_multicall = require("@thru/programs/multicall");
28
29
  async function addDeviceToAccount(params) {
30
+ return addAuthorityToAccount({
31
+ ...params,
32
+ authorityRecord: params.newAuthorityRecord ?? (0, import_passkey_manager.createAuthorityRecord)(params.newAuthority)
33
+ });
34
+ }
35
+ async function addAuthorityToAccount(params) {
29
36
  const status = params.onStatus ?? (() => {
30
37
  });
31
38
  const walletAccount = await params.thru.accounts.get(params.walletAddress);
@@ -69,9 +76,10 @@ async function addDeviceToAccount(params) {
69
76
  programAddress: params.programAddress
70
77
  });
71
78
  const passkeyProgramPubkey = (0, import_passkey_manager.decodeAddress)(params.programAddress);
72
- const addAuthorityInstruction = (0, import_passkey_manager.encodeAddAuthorityInstruction)({
79
+ const encodeAuthorityInstruction = parsed.layout === "legacyAuthority" ? import_passkey_manager.encodeLegacyAddAuthorityInstruction : import_passkey_manager.encodeAddAuthorityInstruction;
80
+ const addAuthorityInstruction = encodeAuthorityInstruction({
73
81
  walletAccountIdx: ctx.walletAccountIdx,
74
- authority: params.newAuthority
82
+ authorityRecord: params.authorityRecord
75
83
  });
76
84
  let targetProgramIdx = ctx.getAccountIndex(passkeyProgramPubkey);
77
85
  let targetInstructionData = addAuthorityInstruction;
@@ -134,6 +142,7 @@ async function addDeviceToAccount(params) {
134
142
  }
135
143
  // Annotate the CommonJS export names for ESM import in node:
136
144
  0 && (module.exports = {
145
+ addAuthorityToAccount,
137
146
  addDeviceToAccount
138
147
  });
139
148
  //# sourceMappingURL=add-device.cjs.map
@@ -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))\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":[]}
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 type AuthorityRecord,\n buildAccountContext,\n createAuthorityRecord,\n createCredentialLookupSeed,\n createValidateChallenge,\n decodeAddress,\n deriveWalletAddress,\n encodeAddAuthorityInstruction,\n encodeLegacyAddAuthorityInstruction,\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 newAuthorityRecord?: AuthorityRecord;\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\nexport interface AddAuthorityParams\n extends Omit<AddDeviceParams, \"newAuthority\" | \"newAuthorityRecord\" | \"credentialId\" | \"walletName\"> {\n /** Complete authority record to append. */\n authorityRecord: AuthorityRecord;\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 return addAuthorityToAccount({\n ...params,\n authorityRecord:\n params.newAuthorityRecord ?? createAuthorityRecord(params.newAuthority),\n });\n}\n\n/**\n * Run VALIDATE + ADD_AUTHORITY to attach a complete authority record to an\n * on-chain WalletAccount.\n */\nexport async function addAuthorityToAccount(\n params: AddAuthorityParams & Pick<AddDeviceParams, \"credentialId\" | \"walletName\">,\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 encodeAuthorityInstruction =\n parsed.layout === \"legacyAuthority\"\n ? encodeLegacyAddAuthorityInstruction\n : encodeAddAuthorityInstruction;\n const addAuthorityInstruction = encodeAuthorityInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n authorityRecord: params.authorityRecord,\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":[]}
@@ -1,4 +1,4 @@
1
- import { Authority, WalletSigner } from '@thru/programs/passkey-manager';
1
+ import { Authority, AuthorityRecord, WalletSigner } from '@thru/programs/passkey-manager';
2
2
 
3
3
  /** Minimal shape required from a passkey signer. Both web's
4
4
  `signWithDiscoverablePasskey`/`signWithPasskey` and mobile's
@@ -25,6 +25,7 @@ interface AddDeviceParams {
25
25
  authIdx: number;
26
26
  /** New passkey to attach. tag = 1 (passkey). */
27
27
  newAuthority: Authority;
28
+ newAuthorityRecord?: AuthorityRecord;
28
29
  /** Optional credential-lookup registration so the new passkey is
29
30
  discoverable on subsequent sign-ins. */
30
31
  credentialId?: Uint8Array;
@@ -60,10 +61,19 @@ interface AddDeviceResult extends TxExecutorResult {
60
61
  /** The new passkey's authority index after the transaction lands. */
61
62
  newAuthorityIdx: number;
62
63
  }
64
+ interface AddAuthorityParams extends Omit<AddDeviceParams, "newAuthority" | "newAuthorityRecord" | "credentialId" | "walletName"> {
65
+ /** Complete authority record to append. */
66
+ authorityRecord: AuthorityRecord;
67
+ }
63
68
  /**
64
69
  * Run VALIDATE + ADD_AUTHORITY [+ REGISTER_CREDENTIAL] to attach a new
65
70
  * passkey to an on-chain WalletAccount.
66
71
  */
67
72
  declare function addDeviceToAccount(params: AddDeviceParams): Promise<AddDeviceResult>;
73
+ /**
74
+ * Run VALIDATE + ADD_AUTHORITY to attach a complete authority record to an
75
+ * on-chain WalletAccount.
76
+ */
77
+ declare function addAuthorityToAccount(params: AddAuthorityParams & Pick<AddDeviceParams, "credentialId" | "walletName">): Promise<AddDeviceResult>;
68
78
 
69
- export { type AddDeviceParams, type AddDeviceResult, type AnyThruClient, type PasskeyChallengeSigner, type TxExecutor, type TxExecutorParams, type TxExecutorResult, addDeviceToAccount };
79
+ export { type AddAuthorityParams, type AddDeviceParams, type AddDeviceResult, type AnyThruClient, type PasskeyChallengeSigner, type TxExecutor, type TxExecutorParams, type TxExecutorResult, addAuthorityToAccount, addDeviceToAccount };
@@ -1,4 +1,4 @@
1
- import { Authority, WalletSigner } from '@thru/programs/passkey-manager';
1
+ import { Authority, AuthorityRecord, WalletSigner } from '@thru/programs/passkey-manager';
2
2
 
3
3
  /** Minimal shape required from a passkey signer. Both web's
4
4
  `signWithDiscoverablePasskey`/`signWithPasskey` and mobile's
@@ -25,6 +25,7 @@ interface AddDeviceParams {
25
25
  authIdx: number;
26
26
  /** New passkey to attach. tag = 1 (passkey). */
27
27
  newAuthority: Authority;
28
+ newAuthorityRecord?: AuthorityRecord;
28
29
  /** Optional credential-lookup registration so the new passkey is
29
30
  discoverable on subsequent sign-ins. */
30
31
  credentialId?: Uint8Array;
@@ -60,10 +61,19 @@ interface AddDeviceResult extends TxExecutorResult {
60
61
  /** The new passkey's authority index after the transaction lands. */
61
62
  newAuthorityIdx: number;
62
63
  }
64
+ interface AddAuthorityParams extends Omit<AddDeviceParams, "newAuthority" | "newAuthorityRecord" | "credentialId" | "walletName"> {
65
+ /** Complete authority record to append. */
66
+ authorityRecord: AuthorityRecord;
67
+ }
63
68
  /**
64
69
  * Run VALIDATE + ADD_AUTHORITY [+ REGISTER_CREDENTIAL] to attach a new
65
70
  * passkey to an on-chain WalletAccount.
66
71
  */
67
72
  declare function addDeviceToAccount(params: AddDeviceParams): Promise<AddDeviceResult>;
73
+ /**
74
+ * Run VALIDATE + ADD_AUTHORITY to attach a complete authority record to an
75
+ * on-chain WalletAccount.
76
+ */
77
+ declare function addAuthorityToAccount(params: AddAuthorityParams & Pick<AddDeviceParams, "credentialId" | "walletName">): Promise<AddDeviceResult>;
68
78
 
69
- export { type AddDeviceParams, type AddDeviceResult, type AnyThruClient, type PasskeyChallengeSigner, type TxExecutor, type TxExecutorParams, type TxExecutorResult, addDeviceToAccount };
79
+ export { type AddAuthorityParams, type AddDeviceParams, type AddDeviceResult, type AnyThruClient, type PasskeyChallengeSigner, type TxExecutor, type TxExecutorParams, type TxExecutorResult, addAuthorityToAccount, addDeviceToAccount };
@@ -1,7 +1,9 @@
1
1
  import {
2
+ addAuthorityToAccount,
2
3
  addDeviceToAccount
3
- } from "../chunk-KASTJBBY.js";
4
+ } from "../chunk-NFK5D7EI.js";
4
5
  export {
6
+ addAuthorityToAccount,
5
7
  addDeviceToAccount
6
8
  };
7
9
  //# sourceMappingURL=add-device.js.map
package/dist/auth.cjs CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/auth/index.ts
31
31
  var auth_exports = {};
32
32
  __export(auth_exports, {
33
+ addAuthorityToAccount: () => addAuthorityToAccount,
33
34
  addDeviceToAccount: () => addDeviceToAccount,
34
35
  createPasskeyAuthStore: () => createPasskeyAuthStore,
35
36
  executePasskeyTransaction: () => executePasskeyTransaction,
@@ -47,7 +48,7 @@ var import_passkey_manager = require("@thru/programs/passkey-manager");
47
48
  function getDefaultConfig(config) {
48
49
  const env = globalThis.process?.env ?? {};
49
50
  return {
50
- rpId: config?.rpId ?? env.EXPO_PUBLIC_PASSKEY_RP_ID ?? "wallet.thru.org",
51
+ rpId: config?.rpId ?? env.EXPO_PUBLIC_PASSKEY_RP_ID ?? "app.tid.sh",
51
52
  rpName: config?.rpName ?? env.EXPO_PUBLIC_PASSKEY_RP_NAME ?? "Thru Wallet"
52
53
  };
53
54
  }
@@ -306,6 +307,12 @@ async function executePasskeyTransaction(opts) {
306
307
  var import_passkey_manager3 = require("@thru/programs/passkey-manager");
307
308
  var import_multicall = require("@thru/programs/multicall");
308
309
  async function addDeviceToAccount(params) {
310
+ return addAuthorityToAccount({
311
+ ...params,
312
+ authorityRecord: params.newAuthorityRecord ?? (0, import_passkey_manager3.createAuthorityRecord)(params.newAuthority)
313
+ });
314
+ }
315
+ async function addAuthorityToAccount(params) {
309
316
  const status = params.onStatus ?? (() => {
310
317
  });
311
318
  const walletAccount = await params.thru.accounts.get(params.walletAddress);
@@ -349,9 +356,10 @@ async function addDeviceToAccount(params) {
349
356
  programAddress: params.programAddress
350
357
  });
351
358
  const passkeyProgramPubkey = (0, import_passkey_manager3.decodeAddress)(params.programAddress);
352
- const addAuthorityInstruction = (0, import_passkey_manager3.encodeAddAuthorityInstruction)({
359
+ const encodeAuthorityInstruction = parsed.layout === "legacyAuthority" ? import_passkey_manager3.encodeLegacyAddAuthorityInstruction : import_passkey_manager3.encodeAddAuthorityInstruction;
360
+ const addAuthorityInstruction = encodeAuthorityInstruction({
353
361
  walletAccountIdx: ctx.walletAccountIdx,
354
- authority: params.newAuthority
362
+ authorityRecord: params.authorityRecord
355
363
  });
356
364
  let targetProgramIdx = ctx.getAccountIndex(passkeyProgramPubkey);
357
365
  let targetInstructionData = addAuthorityInstruction;
@@ -778,6 +786,7 @@ function usePasskeyAuth(config) {
778
786
  }
779
787
  // Annotate the CommonJS export names for ESM import in node:
780
788
  0 && (module.exports = {
789
+ addAuthorityToAccount,
781
790
  addDeviceToAccount,
782
791
  createPasskeyAuthStore,
783
792
  executePasskeyTransaction,
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))\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"]}
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 { addAuthorityToAccount, addDeviceToAccount } from './add-device';\nexport type {\n AddAuthorityParams,\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 ?? 'app.tid.sh',\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 type AuthorityRecord,\n buildAccountContext,\n createAuthorityRecord,\n createCredentialLookupSeed,\n createValidateChallenge,\n decodeAddress,\n deriveWalletAddress,\n encodeAddAuthorityInstruction,\n encodeLegacyAddAuthorityInstruction,\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 newAuthorityRecord?: AuthorityRecord;\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\nexport interface AddAuthorityParams\n extends Omit<AddDeviceParams, \"newAuthority\" | \"newAuthorityRecord\" | \"credentialId\" | \"walletName\"> {\n /** Complete authority record to append. */\n authorityRecord: AuthorityRecord;\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 return addAuthorityToAccount({\n ...params,\n authorityRecord:\n params.newAuthorityRecord ?? createAuthorityRecord(params.newAuthority),\n });\n}\n\n/**\n * Run VALIDATE + ADD_AUTHORITY to attach a complete authority record to an\n * on-chain WalletAccount.\n */\nexport async function addAuthorityToAccount(\n params: AddAuthorityParams & Pick<AddDeviceParams, \"credentialId\" | \"walletName\">,\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 encodeAuthorityInstruction =\n parsed.layout === \"legacyAuthority\"\n ? encodeLegacyAddAuthorityInstruction\n : encodeAddAuthorityInstruction;\n const addAuthorityInstruction = encodeAuthorityInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n authorityRecord: params.authorityRecord,\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.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { UseBoundStore, StoreApi } from 'zustand';
2
- export { AddDeviceParams, AddDeviceResult, AnyThruClient, PasskeyChallengeSigner, TxExecutor, TxExecutorParams, TxExecutorResult, addDeviceToAccount } from './auth/add-device.cjs';
2
+ export { AddAuthorityParams, AddDeviceParams, AddDeviceResult, AnyThruClient, PasskeyChallengeSigner, TxExecutor, TxExecutorParams, TxExecutorResult, addAuthorityToAccount, addDeviceToAccount } from './auth/add-device.cjs';
3
3
  import '@thru/programs/passkey-manager';
4
4
 
5
5
  interface PasskeyApiUser<TExtra = Record<string, never>> {
package/dist/auth.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { UseBoundStore, StoreApi } from 'zustand';
2
- export { AddDeviceParams, AddDeviceResult, AnyThruClient, PasskeyChallengeSigner, TxExecutor, TxExecutorParams, TxExecutorResult, addDeviceToAccount } from './auth/add-device.js';
2
+ export { AddAuthorityParams, AddDeviceParams, AddDeviceResult, AnyThruClient, PasskeyChallengeSigner, TxExecutor, TxExecutorParams, TxExecutorResult, addAuthorityToAccount, addDeviceToAccount } from './auth/add-device.js';
3
3
  import '@thru/programs/passkey-manager';
4
4
 
5
5
  interface PasskeyApiUser<TExtra = Record<string, never>> {
package/dist/auth.js CHANGED
@@ -13,10 +13,11 @@ import {
13
13
  storePasskeyMetadata,
14
14
  storeWalletInfo,
15
15
  touchPasskeyLastUsedAt
16
- } from "./chunk-ZNBMADOM.js";
16
+ } from "./chunk-L53S4RKQ.js";
17
17
  import {
18
+ addAuthorityToAccount,
18
19
  addDeviceToAccount
19
- } from "./chunk-KASTJBBY.js";
20
+ } from "./chunk-NFK5D7EI.js";
20
21
 
21
22
  // src/auth/execute-tx.ts
22
23
  import { base64UrlToBytes, bytesToBase64, bytesToHex } from "@thru/programs/passkey-manager";
@@ -419,6 +420,7 @@ function usePasskeyAuth(config) {
419
420
  return getPasskeyAuthStore(config)();
420
421
  }
421
422
  export {
423
+ addAuthorityToAccount,
422
424
  addDeviceToAccount,
423
425
  createPasskeyAuthStore,
424
426
  executePasskeyTransaction,