@thru/passkey 0.2.20 → 0.2.22

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 (79) hide show
  1. package/dist/auth/add-device.cjs +118 -0
  2. package/dist/auth/add-device.cjs.map +1 -0
  3. package/dist/auth/add-device.d.cts +69 -0
  4. package/dist/auth/add-device.d.ts +69 -0
  5. package/dist/auth/add-device.js +7 -0
  6. package/dist/auth/add-device.js.map +1 -0
  7. package/dist/auth.cjs +100 -6
  8. package/dist/auth.cjs.map +1 -1
  9. package/dist/auth.d.cts +2 -0
  10. package/dist/auth.d.ts +2 -0
  11. package/dist/auth.js +10 -4
  12. package/dist/auth.js.map +1 -1
  13. package/dist/{chunk-75G2FPYW.js → chunk-OULTQZT7.js} +4 -3
  14. package/dist/chunk-OULTQZT7.js.map +1 -0
  15. package/dist/chunk-QAQNRQ7G.js +104 -0
  16. package/dist/chunk-QAQNRQ7G.js.map +1 -0
  17. package/dist/{chunk-B5SN7AS7.js → chunk-TW7HANJM.js} +99 -49
  18. package/dist/chunk-TW7HANJM.js.map +1 -0
  19. package/dist/{chunk-2JHC7OOH.js → chunk-ZNBMADOM.js} +2 -2
  20. package/dist/chunk-ZNBMADOM.js.map +1 -0
  21. package/dist/index.cjs +102 -50
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +3 -3
  24. package/dist/index.d.ts +3 -3
  25. package/dist/index.js +4 -2
  26. package/dist/mobile.cjs +2 -2
  27. package/dist/mobile.cjs.map +1 -1
  28. package/dist/mobile.d.cts +2 -2
  29. package/dist/mobile.d.ts +2 -2
  30. package/dist/mobile.js +2 -2
  31. package/dist/mobile.js.map +1 -1
  32. package/dist/popup.cjs +3 -2
  33. package/dist/popup.cjs.map +1 -1
  34. package/dist/popup.d.cts +3 -3
  35. package/dist/popup.d.ts +3 -3
  36. package/dist/popup.js +1 -1
  37. package/dist/server.cjs +30 -60
  38. package/dist/server.cjs.map +1 -1
  39. package/dist/server.d.cts +2 -2
  40. package/dist/server.d.ts +2 -2
  41. package/dist/server.js +32 -62
  42. package/dist/server.js.map +1 -1
  43. package/dist/{types-_HRzmn-j.d.cts → types-BTTlCVrw.d.cts} +25 -2
  44. package/dist/{types-_HRzmn-j.d.ts → types-BTTlCVrw.d.ts} +25 -2
  45. package/dist/web.cjs +99 -48
  46. package/dist/web.cjs.map +1 -1
  47. package/dist/web.d.cts +14 -7
  48. package/dist/web.d.ts +14 -7
  49. package/dist/web.js +3 -1
  50. package/package.json +11 -6
  51. package/src/auth/add-device.ts +213 -0
  52. package/src/auth/execute-tx.ts +1 -1
  53. package/src/auth/index.ts +11 -0
  54. package/src/auth/use-passkey-auth.ts +4 -2
  55. package/src/capabilities.ts +2 -1
  56. package/src/index.ts +4 -0
  57. package/src/label.test.ts +21 -0
  58. package/src/label.ts +14 -0
  59. package/src/mobile/index.ts +1 -1
  60. package/src/mobile/passkey.ts +1 -1
  61. package/src/mobile/storage.ts +1 -1
  62. package/src/mobile/types.ts +2 -2
  63. package/src/popup-service.ts +2 -1
  64. package/src/register.ts +23 -8
  65. package/src/server/challenge.ts +2 -2
  66. package/src/server/create-wallet.test.ts +2 -2
  67. package/src/server/create-wallet.ts +24 -16
  68. package/src/server/submit.test.ts +2 -2
  69. package/src/server/submit.ts +25 -4
  70. package/src/server/types.ts +2 -2
  71. package/src/server/utils.ts +1 -1
  72. package/src/sign.ts +127 -37
  73. package/src/types.ts +27 -2
  74. package/src/web.ts +6 -2
  75. package/tsconfig.json +3 -2
  76. package/tsup.config.ts +1 -0
  77. package/dist/chunk-2JHC7OOH.js.map +0 -1
  78. package/dist/chunk-75G2FPYW.js.map +0 -1
  79. package/dist/chunk-B5SN7AS7.js.map +0 -1
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/auth/add-device.ts
21
+ var add_device_exports = {};
22
+ __export(add_device_exports, {
23
+ addDeviceToAccount: () => addDeviceToAccount
24
+ });
25
+ module.exports = __toCommonJS(add_device_exports);
26
+ var import_passkey_manager = require("@thru/programs/passkey-manager");
27
+ async function addDeviceToAccount(params) {
28
+ const status = params.onStatus ?? (() => {
29
+ });
30
+ const walletAccount = await params.thru.accounts.get(params.walletAddress);
31
+ const walletData = walletAccount?.data?.data;
32
+ if (!walletData) {
33
+ throw new Error("Wallet account data missing");
34
+ }
35
+ const parsed = (0, import_passkey_manager.parseWalletAuthorities)(walletData);
36
+ const authorizing = parsed.authorities[params.authIdx];
37
+ if (!authorizing) {
38
+ throw new Error("Authorization index out of bounds");
39
+ }
40
+ if (authorizing.kind !== "passkey") {
41
+ throw new Error(
42
+ "addDeviceToAccount currently requires a passkey authority for VALIDATE"
43
+ );
44
+ }
45
+ const newAuthorityIdx = parsed.authorities.length;
46
+ const addAuthorityInstruction = (0, import_passkey_manager.encodeAddAuthorityInstruction)({
47
+ authority: params.newAuthority
48
+ });
49
+ let trailingInstructionData = addAuthorityInstruction;
50
+ let readWriteAccounts = [];
51
+ if (params.credentialId) {
52
+ const lookupSeed = await (0, import_passkey_manager.createCredentialLookupSeed)(params.credentialId);
53
+ const lookupAddressBytes = await (0, import_passkey_manager.deriveWalletAddress)(
54
+ lookupSeed,
55
+ params.programAddress
56
+ );
57
+ status("Fetching state proof for credential lookup...");
58
+ const proofResult = await params.thru.proofs.generate({
59
+ proofType: 1,
60
+ address: lookupAddressBytes
61
+ });
62
+ const ctx2 = (0, import_passkey_manager.buildAccountContext)({
63
+ walletAddress: params.walletAddress,
64
+ readWriteAccounts: [lookupAddressBytes],
65
+ readOnlyAccounts: []
66
+ });
67
+ const registerCredentialInstruction = (0, import_passkey_manager.encodeRegisterCredentialInstruction)({
68
+ walletAccountIdx: ctx2.walletAccountIdx,
69
+ lookupAccountIdx: ctx2.getAccountIndex(lookupAddressBytes),
70
+ seed: lookupSeed,
71
+ stateProof: proofResult.proof
72
+ });
73
+ trailingInstructionData = (0, import_passkey_manager.concatenateInstructions)([
74
+ addAuthorityInstruction,
75
+ registerCredentialInstruction
76
+ ]);
77
+ readWriteAccounts = [lookupAddressBytes];
78
+ }
79
+ const ctx = (0, import_passkey_manager.buildAccountContext)({
80
+ walletAddress: params.walletAddress,
81
+ readWriteAccounts,
82
+ readOnlyAccounts: []
83
+ });
84
+ const challenge = await (0, import_passkey_manager.createValidateChallenge)(
85
+ parsed.nonce,
86
+ ctx.accountAddresses,
87
+ trailingInstructionData
88
+ );
89
+ status("Waiting for passkey approval...");
90
+ const signature = await params.passkey.signChallenge(challenge);
91
+ const validateInstruction = (0, import_passkey_manager.encodeValidateInstruction)({
92
+ walletAccountIdx: ctx.walletAccountIdx,
93
+ authIdx: params.authIdx,
94
+ signatureR: signature.signatureR,
95
+ signatureS: signature.signatureS,
96
+ authenticatorData: signature.authenticatorData,
97
+ clientDataJSON: signature.clientDataJSON
98
+ });
99
+ const instructionData = (0, import_passkey_manager.concatenateInstructions)([
100
+ validateInstruction,
101
+ trailingInstructionData
102
+ ]);
103
+ status("Sending transaction...");
104
+ const result = await params.executor({
105
+ thru: params.thru,
106
+ walletSigner: params.walletSigner,
107
+ instructionData,
108
+ readWriteAddresses: ctx.readWriteAddresses,
109
+ readOnlyAddresses: ctx.readOnlyAddresses,
110
+ label: params.credentialId ? "VALIDATE + ADD_AUTHORITY + REGISTER_CREDENTIAL" : "VALIDATE + ADD_AUTHORITY"
111
+ });
112
+ return { ...result, newAuthorityIdx };
113
+ }
114
+ // Annotate the CommonJS export names for ESM import in node:
115
+ 0 && (module.exports = {
116
+ addDeviceToAccount
117
+ });
118
+ //# sourceMappingURL=add-device.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/auth/add-device.ts"],"sourcesContent":["/* Add-passkey-to-account transaction builder, lifted from\n `web/wallet-auth-manager/app/page.tsx` (`runValidateThen`,\n `handleSubmitAddPasskey`) so the wallet's `/embedded` post-connect\n step can reuse the exact same flow.\n\n Builds the on-chain transaction:\n VALIDATE(existingAuthority) + ADD_AUTHORITY(newPasskey) [+ REGISTER_CREDENTIAL]\n asks the caller's existing passkey to sign the challenge, then asks\n the caller's wallet signer to sign the assembled transaction, sends\n it, and returns the result. */\n\nimport {\n type Authority,\n buildAccountContext,\n concatenateInstructions,\n createValidateChallenge,\n createCredentialLookupSeed,\n deriveWalletAddress,\n encodeAddAuthorityInstruction,\n encodeRegisterCredentialInstruction,\n encodeValidateInstruction,\n parseWalletAuthorities,\n type ParsedAuthority,\n type WalletSigner,\n} from \"@thru/programs/passkey-manager\";\n\n/** Minimal shape required from a passkey signer. Both web's\n `signWithDiscoverablePasskey`/`signWithPasskey` and mobile's\n counterparts conform. */\nexport interface PasskeyChallengeSigner {\n signChallenge: (challenge: Uint8Array) => Promise<{\n signatureR: Uint8Array;\n signatureS: Uint8Array;\n authenticatorData: Uint8Array;\n clientDataJSON: Uint8Array;\n }>;\n}\n\n/** Minimal shape required from a Thru chain client. Loosely-typed\n because @thru/sdk's DTS emit is currently broken in this\n repo. The caller passes the real Thru and we narrow operationally. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyThruClient = any;\n\nexport interface AddDeviceParams {\n /** Loosely-typed Thru chain client (`@thru/sdk/client`). */\n thru: AnyThruClient;\n /** Wallet (the on-chain WalletAccount) to attach the passkey to. */\n walletAddress: string;\n /** Index of the existing authority that approves this change. Must\n currently be a passkey authority. */\n authIdx: number;\n /** New passkey to attach. tag = 1 (passkey). */\n newAuthority: Authority;\n /** Optional credential-lookup registration so the new passkey is\n discoverable on subsequent sign-ins. */\n credentialId?: Uint8Array;\n walletName?: string;\n /** Existing-passkey challenge signer (web or mobile). */\n passkey: PasskeyChallengeSigner;\n /** Wallet transaction signer that returns base64(signed bytes). */\n walletSigner: WalletSigner;\n /** Passkey program address (base58). */\n programAddress: string;\n /** Sign-and-send executor (lifted from passkey-transaction.ts in the\n wallet-auth-manager - wallet apps own this because it depends on\n the `Thru` client's transaction builder). */\n executor: TxExecutor;\n /** Optional status callback so UIs can show progress. */\n onStatus?: (message: string) => void;\n}\n\nexport interface TxExecutorParams {\n thru: AnyThruClient;\n walletSigner: WalletSigner;\n instructionData: Uint8Array;\n readWriteAddresses: string[];\n readOnlyAddresses: string[];\n label: string;\n}\n\nexport interface TxExecutorResult {\n signature: string;\n /** Loosely-typed because @thru/sdk types aren't available. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n execution: any;\n}\n\nexport type TxExecutor = (\n params: TxExecutorParams,\n) => Promise<TxExecutorResult>;\n\nexport interface AddDeviceResult extends TxExecutorResult {\n /** The new passkey's authority index after the transaction lands. */\n newAuthorityIdx: number;\n}\n\n/**\n * Run VALIDATE + ADD_AUTHORITY [+ REGISTER_CREDENTIAL] to attach a new\n * passkey to an on-chain WalletAccount.\n */\nexport async function addDeviceToAccount(\n params: AddDeviceParams,\n): Promise<AddDeviceResult> {\n const status = params.onStatus ?? (() => {});\n\n const walletAccount = await params.thru.accounts.get(params.walletAddress);\n const walletData: Uint8Array | undefined = walletAccount?.data?.data;\n if (!walletData) {\n throw new Error(\"Wallet account data missing\");\n }\n\n const parsed = parseWalletAuthorities(walletData);\n const authorizing: ParsedAuthority | undefined =\n parsed.authorities[params.authIdx];\n if (!authorizing) {\n throw new Error(\"Authorization index out of bounds\");\n }\n if (authorizing.kind !== \"passkey\") {\n throw new Error(\n \"addDeviceToAccount currently requires a passkey authority for VALIDATE\",\n );\n }\n\n /* The new authority will land at the next free slot. */\n const newAuthorityIdx = parsed.authorities.length;\n\n /* Build the instruction sequence after the VALIDATE. */\n const addAuthorityInstruction = encodeAddAuthorityInstruction({\n authority: params.newAuthority,\n });\n\n let trailingInstructionData = addAuthorityInstruction;\n let readWriteAccounts: Uint8Array[] = [];\n\n if (params.credentialId) {\n const lookupSeed = await createCredentialLookupSeed(params.credentialId);\n const lookupAddressBytes = await deriveWalletAddress(\n lookupSeed,\n params.programAddress,\n );\n\n status(\"Fetching state proof for credential lookup...\");\n const proofResult = await params.thru.proofs.generate({\n proofType: 1 /* StateProofType.CREATING */,\n address: lookupAddressBytes,\n });\n\n const ctx = buildAccountContext({\n walletAddress: params.walletAddress,\n readWriteAccounts: [lookupAddressBytes],\n readOnlyAccounts: [],\n });\n\n const registerCredentialInstruction = encodeRegisterCredentialInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n lookupAccountIdx: ctx.getAccountIndex(lookupAddressBytes),\n seed: lookupSeed,\n stateProof: proofResult.proof,\n });\n\n trailingInstructionData = concatenateInstructions([\n addAuthorityInstruction,\n registerCredentialInstruction,\n ]);\n readWriteAccounts = [lookupAddressBytes];\n }\n\n /* Build the VALIDATE challenge over (nonce, account_addresses,\n trailing_instruction_data) and ask the caller's passkey to sign. */\n const ctx = buildAccountContext({\n walletAddress: params.walletAddress,\n readWriteAccounts,\n readOnlyAccounts: [],\n });\n\n const challenge = await createValidateChallenge(\n parsed.nonce,\n ctx.accountAddresses,\n trailingInstructionData,\n );\n\n status(\"Waiting for passkey approval...\");\n const signature = await params.passkey.signChallenge(challenge);\n\n const validateInstruction = encodeValidateInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n authIdx: params.authIdx,\n signatureR: signature.signatureR,\n signatureS: signature.signatureS,\n authenticatorData: signature.authenticatorData,\n clientDataJSON: signature.clientDataJSON,\n });\n\n const instructionData = concatenateInstructions([\n validateInstruction,\n trailingInstructionData,\n ]);\n\n status(\"Sending transaction...\");\n const result = await params.executor({\n thru: params.thru,\n walletSigner: params.walletSigner,\n instructionData,\n readWriteAddresses: ctx.readWriteAddresses,\n readOnlyAddresses: ctx.readOnlyAddresses,\n label: params.credentialId\n ? \"VALIDATE + ADD_AUTHORITY + REGISTER_CREDENTIAL\"\n : \"VALIDATE + ADD_AUTHORITY\",\n });\n\n return { ...result, newAuthorityIdx };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,6BAaO;AA6EP,eAAsB,mBACpB,QAC0B;AAC1B,QAAM,SAAS,OAAO,aAAa,MAAM;AAAA,EAAC;AAE1C,QAAM,gBAAgB,MAAM,OAAO,KAAK,SAAS,IAAI,OAAO,aAAa;AACzE,QAAM,aAAqC,eAAe,MAAM;AAChE,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,6BAA6B;AAAA,EAC/C;AAEA,QAAM,aAAS,+CAAuB,UAAU;AAChD,QAAM,cACJ,OAAO,YAAY,OAAO,OAAO;AACnC,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AACA,MAAI,YAAY,SAAS,WAAW;AAClC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAGA,QAAM,kBAAkB,OAAO,YAAY;AAG3C,QAAM,8BAA0B,sDAA8B;AAAA,IAC5D,WAAW,OAAO;AAAA,EACpB,CAAC;AAED,MAAI,0BAA0B;AAC9B,MAAI,oBAAkC,CAAC;AAEvC,MAAI,OAAO,cAAc;AACvB,UAAM,aAAa,UAAM,mDAA2B,OAAO,YAAY;AACvE,UAAM,qBAAqB,UAAM;AAAA,MAC/B;AAAA,MACA,OAAO;AAAA,IACT;AAEA,WAAO,+CAA+C;AACtD,UAAM,cAAc,MAAM,OAAO,KAAK,OAAO,SAAS;AAAA,MACpD,WAAW;AAAA,MACX,SAAS;AAAA,IACX,CAAC;AAED,UAAMA,WAAM,4CAAoB;AAAA,MAC9B,eAAe,OAAO;AAAA,MACtB,mBAAmB,CAAC,kBAAkB;AAAA,MACtC,kBAAkB,CAAC;AAAA,IACrB,CAAC;AAED,UAAM,oCAAgC,4DAAoC;AAAA,MACxE,kBAAkBA,KAAI;AAAA,MACtB,kBAAkBA,KAAI,gBAAgB,kBAAkB;AAAA,MACxD,MAAM;AAAA,MACN,YAAY,YAAY;AAAA,IAC1B,CAAC;AAED,kCAA0B,gDAAwB;AAAA,MAChD;AAAA,MACA;AAAA,IACF,CAAC;AACD,wBAAoB,CAAC,kBAAkB;AAAA,EACzC;AAIA,QAAM,UAAM,4CAAoB;AAAA,IAC9B,eAAe,OAAO;AAAA,IACtB;AAAA,IACA,kBAAkB,CAAC;AAAA,EACrB,CAAC;AAED,QAAM,YAAY,UAAM;AAAA,IACtB,OAAO;AAAA,IACP,IAAI;AAAA,IACJ;AAAA,EACF;AAEA,SAAO,iCAAiC;AACxC,QAAM,YAAY,MAAM,OAAO,QAAQ,cAAc,SAAS;AAE9D,QAAM,0BAAsB,kDAA0B;AAAA,IACpD,kBAAkB,IAAI;AAAA,IACtB,SAAS,OAAO;AAAA,IAChB,YAAY,UAAU;AAAA,IACtB,YAAY,UAAU;AAAA,IACtB,mBAAmB,UAAU;AAAA,IAC7B,gBAAgB,UAAU;AAAA,EAC5B,CAAC;AAED,QAAM,sBAAkB,gDAAwB;AAAA,IAC9C;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO,wBAAwB;AAC/B,QAAM,SAAS,MAAM,OAAO,SAAS;AAAA,IACnC,MAAM,OAAO;AAAA,IACb,cAAc,OAAO;AAAA,IACrB;AAAA,IACA,oBAAoB,IAAI;AAAA,IACxB,mBAAmB,IAAI;AAAA,IACvB,OAAO,OAAO,eACV,mDACA;AAAA,EACN,CAAC;AAED,SAAO,EAAE,GAAG,QAAQ,gBAAgB;AACtC;","names":["ctx"]}
@@ -0,0 +1,69 @@
1
+ import { Authority, WalletSigner } from '@thru/programs/passkey-manager';
2
+
3
+ /** Minimal shape required from a passkey signer. Both web's
4
+ `signWithDiscoverablePasskey`/`signWithPasskey` and mobile's
5
+ counterparts conform. */
6
+ interface PasskeyChallengeSigner {
7
+ signChallenge: (challenge: Uint8Array) => Promise<{
8
+ signatureR: Uint8Array;
9
+ signatureS: Uint8Array;
10
+ authenticatorData: Uint8Array;
11
+ clientDataJSON: Uint8Array;
12
+ }>;
13
+ }
14
+ /** Minimal shape required from a Thru chain client. Loosely-typed
15
+ because @thru/sdk's DTS emit is currently broken in this
16
+ repo. The caller passes the real Thru and we narrow operationally. */
17
+ type AnyThruClient = any;
18
+ interface AddDeviceParams {
19
+ /** Loosely-typed Thru chain client (`@thru/sdk/client`). */
20
+ thru: AnyThruClient;
21
+ /** Wallet (the on-chain WalletAccount) to attach the passkey to. */
22
+ walletAddress: string;
23
+ /** Index of the existing authority that approves this change. Must
24
+ currently be a passkey authority. */
25
+ authIdx: number;
26
+ /** New passkey to attach. tag = 1 (passkey). */
27
+ newAuthority: Authority;
28
+ /** Optional credential-lookup registration so the new passkey is
29
+ discoverable on subsequent sign-ins. */
30
+ credentialId?: Uint8Array;
31
+ walletName?: string;
32
+ /** Existing-passkey challenge signer (web or mobile). */
33
+ passkey: PasskeyChallengeSigner;
34
+ /** Wallet transaction signer that returns base64(signed bytes). */
35
+ walletSigner: WalletSigner;
36
+ /** Passkey program address (base58). */
37
+ programAddress: string;
38
+ /** Sign-and-send executor (lifted from passkey-transaction.ts in the
39
+ wallet-auth-manager - wallet apps own this because it depends on
40
+ the `Thru` client's transaction builder). */
41
+ executor: TxExecutor;
42
+ /** Optional status callback so UIs can show progress. */
43
+ onStatus?: (message: string) => void;
44
+ }
45
+ interface TxExecutorParams {
46
+ thru: AnyThruClient;
47
+ walletSigner: WalletSigner;
48
+ instructionData: Uint8Array;
49
+ readWriteAddresses: string[];
50
+ readOnlyAddresses: string[];
51
+ label: string;
52
+ }
53
+ interface TxExecutorResult {
54
+ signature: string;
55
+ /** Loosely-typed because @thru/sdk types aren't available. */
56
+ execution: any;
57
+ }
58
+ type TxExecutor = (params: TxExecutorParams) => Promise<TxExecutorResult>;
59
+ interface AddDeviceResult extends TxExecutorResult {
60
+ /** The new passkey's authority index after the transaction lands. */
61
+ newAuthorityIdx: number;
62
+ }
63
+ /**
64
+ * Run VALIDATE + ADD_AUTHORITY [+ REGISTER_CREDENTIAL] to attach a new
65
+ * passkey to an on-chain WalletAccount.
66
+ */
67
+ declare function addDeviceToAccount(params: AddDeviceParams): Promise<AddDeviceResult>;
68
+
69
+ export { type AddDeviceParams, type AddDeviceResult, type AnyThruClient, type PasskeyChallengeSigner, type TxExecutor, type TxExecutorParams, type TxExecutorResult, addDeviceToAccount };
@@ -0,0 +1,69 @@
1
+ import { Authority, WalletSigner } from '@thru/programs/passkey-manager';
2
+
3
+ /** Minimal shape required from a passkey signer. Both web's
4
+ `signWithDiscoverablePasskey`/`signWithPasskey` and mobile's
5
+ counterparts conform. */
6
+ interface PasskeyChallengeSigner {
7
+ signChallenge: (challenge: Uint8Array) => Promise<{
8
+ signatureR: Uint8Array;
9
+ signatureS: Uint8Array;
10
+ authenticatorData: Uint8Array;
11
+ clientDataJSON: Uint8Array;
12
+ }>;
13
+ }
14
+ /** Minimal shape required from a Thru chain client. Loosely-typed
15
+ because @thru/sdk's DTS emit is currently broken in this
16
+ repo. The caller passes the real Thru and we narrow operationally. */
17
+ type AnyThruClient = any;
18
+ interface AddDeviceParams {
19
+ /** Loosely-typed Thru chain client (`@thru/sdk/client`). */
20
+ thru: AnyThruClient;
21
+ /** Wallet (the on-chain WalletAccount) to attach the passkey to. */
22
+ walletAddress: string;
23
+ /** Index of the existing authority that approves this change. Must
24
+ currently be a passkey authority. */
25
+ authIdx: number;
26
+ /** New passkey to attach. tag = 1 (passkey). */
27
+ newAuthority: Authority;
28
+ /** Optional credential-lookup registration so the new passkey is
29
+ discoverable on subsequent sign-ins. */
30
+ credentialId?: Uint8Array;
31
+ walletName?: string;
32
+ /** Existing-passkey challenge signer (web or mobile). */
33
+ passkey: PasskeyChallengeSigner;
34
+ /** Wallet transaction signer that returns base64(signed bytes). */
35
+ walletSigner: WalletSigner;
36
+ /** Passkey program address (base58). */
37
+ programAddress: string;
38
+ /** Sign-and-send executor (lifted from passkey-transaction.ts in the
39
+ wallet-auth-manager - wallet apps own this because it depends on
40
+ the `Thru` client's transaction builder). */
41
+ executor: TxExecutor;
42
+ /** Optional status callback so UIs can show progress. */
43
+ onStatus?: (message: string) => void;
44
+ }
45
+ interface TxExecutorParams {
46
+ thru: AnyThruClient;
47
+ walletSigner: WalletSigner;
48
+ instructionData: Uint8Array;
49
+ readWriteAddresses: string[];
50
+ readOnlyAddresses: string[];
51
+ label: string;
52
+ }
53
+ interface TxExecutorResult {
54
+ signature: string;
55
+ /** Loosely-typed because @thru/sdk types aren't available. */
56
+ execution: any;
57
+ }
58
+ type TxExecutor = (params: TxExecutorParams) => Promise<TxExecutorResult>;
59
+ interface AddDeviceResult extends TxExecutorResult {
60
+ /** The new passkey's authority index after the transaction lands. */
61
+ newAuthorityIdx: number;
62
+ }
63
+ /**
64
+ * Run VALIDATE + ADD_AUTHORITY [+ REGISTER_CREDENTIAL] to attach a new
65
+ * passkey to an on-chain WalletAccount.
66
+ */
67
+ declare function addDeviceToAccount(params: AddDeviceParams): Promise<AddDeviceResult>;
68
+
69
+ export { type AddDeviceParams, type AddDeviceResult, type AnyThruClient, type PasskeyChallengeSigner, type TxExecutor, type TxExecutorParams, type TxExecutorResult, addDeviceToAccount };
@@ -0,0 +1,7 @@
1
+ import {
2
+ addDeviceToAccount
3
+ } from "../chunk-QAQNRQ7G.js";
4
+ export {
5
+ addDeviceToAccount
6
+ };
7
+ //# sourceMappingURL=add-device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
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
+ addDeviceToAccount: () => addDeviceToAccount,
33
34
  createPasskeyAuthStore: () => createPasskeyAuthStore,
34
35
  executePasskeyTransaction: () => executePasskeyTransaction,
35
36
  getPasskeyAuthStore: () => getPasskeyAuthStore,
@@ -38,11 +39,11 @@ __export(auth_exports, {
38
39
  module.exports = __toCommonJS(auth_exports);
39
40
 
40
41
  // src/auth/execute-tx.ts
41
- var import_passkey_manager2 = require("@thru/passkey-manager");
42
+ var import_passkey_manager2 = require("@thru/programs/passkey-manager");
42
43
 
43
44
  // src/mobile/passkey.ts
44
45
  var import_react_native_passkeys = require("react-native-passkeys");
45
- var import_passkey_manager = require("@thru/passkey-manager");
46
+ var import_passkey_manager = require("@thru/programs/passkey-manager");
46
47
  function getDefaultConfig(config) {
47
48
  const env = globalThis.process?.env ?? {};
48
49
  return {
@@ -301,8 +302,98 @@ async function executePasskeyTransaction(opts) {
301
302
  return submitData;
302
303
  }
303
304
 
305
+ // src/auth/add-device.ts
306
+ var import_passkey_manager3 = require("@thru/programs/passkey-manager");
307
+ async function addDeviceToAccount(params) {
308
+ const status = params.onStatus ?? (() => {
309
+ });
310
+ const walletAccount = await params.thru.accounts.get(params.walletAddress);
311
+ const walletData = walletAccount?.data?.data;
312
+ if (!walletData) {
313
+ throw new Error("Wallet account data missing");
314
+ }
315
+ const parsed = (0, import_passkey_manager3.parseWalletAuthorities)(walletData);
316
+ const authorizing = parsed.authorities[params.authIdx];
317
+ if (!authorizing) {
318
+ throw new Error("Authorization index out of bounds");
319
+ }
320
+ if (authorizing.kind !== "passkey") {
321
+ throw new Error(
322
+ "addDeviceToAccount currently requires a passkey authority for VALIDATE"
323
+ );
324
+ }
325
+ const newAuthorityIdx = parsed.authorities.length;
326
+ const addAuthorityInstruction = (0, import_passkey_manager3.encodeAddAuthorityInstruction)({
327
+ authority: params.newAuthority
328
+ });
329
+ let trailingInstructionData = addAuthorityInstruction;
330
+ let readWriteAccounts = [];
331
+ if (params.credentialId) {
332
+ const lookupSeed = await (0, import_passkey_manager3.createCredentialLookupSeed)(params.credentialId);
333
+ const lookupAddressBytes = await (0, import_passkey_manager3.deriveWalletAddress)(
334
+ lookupSeed,
335
+ params.programAddress
336
+ );
337
+ status("Fetching state proof for credential lookup...");
338
+ const proofResult = await params.thru.proofs.generate({
339
+ proofType: 1,
340
+ address: lookupAddressBytes
341
+ });
342
+ const ctx2 = (0, import_passkey_manager3.buildAccountContext)({
343
+ walletAddress: params.walletAddress,
344
+ readWriteAccounts: [lookupAddressBytes],
345
+ readOnlyAccounts: []
346
+ });
347
+ const registerCredentialInstruction = (0, import_passkey_manager3.encodeRegisterCredentialInstruction)({
348
+ walletAccountIdx: ctx2.walletAccountIdx,
349
+ lookupAccountIdx: ctx2.getAccountIndex(lookupAddressBytes),
350
+ seed: lookupSeed,
351
+ stateProof: proofResult.proof
352
+ });
353
+ trailingInstructionData = (0, import_passkey_manager3.concatenateInstructions)([
354
+ addAuthorityInstruction,
355
+ registerCredentialInstruction
356
+ ]);
357
+ readWriteAccounts = [lookupAddressBytes];
358
+ }
359
+ const ctx = (0, import_passkey_manager3.buildAccountContext)({
360
+ walletAddress: params.walletAddress,
361
+ readWriteAccounts,
362
+ readOnlyAccounts: []
363
+ });
364
+ const challenge = await (0, import_passkey_manager3.createValidateChallenge)(
365
+ parsed.nonce,
366
+ ctx.accountAddresses,
367
+ trailingInstructionData
368
+ );
369
+ status("Waiting for passkey approval...");
370
+ const signature = await params.passkey.signChallenge(challenge);
371
+ const validateInstruction = (0, import_passkey_manager3.encodeValidateInstruction)({
372
+ walletAccountIdx: ctx.walletAccountIdx,
373
+ authIdx: params.authIdx,
374
+ signatureR: signature.signatureR,
375
+ signatureS: signature.signatureS,
376
+ authenticatorData: signature.authenticatorData,
377
+ clientDataJSON: signature.clientDataJSON
378
+ });
379
+ const instructionData = (0, import_passkey_manager3.concatenateInstructions)([
380
+ validateInstruction,
381
+ trailingInstructionData
382
+ ]);
383
+ status("Sending transaction...");
384
+ const result = await params.executor({
385
+ thru: params.thru,
386
+ walletSigner: params.walletSigner,
387
+ instructionData,
388
+ readWriteAddresses: ctx.readWriteAddresses,
389
+ readOnlyAddresses: ctx.readOnlyAddresses,
390
+ label: params.credentialId ? "VALIDATE + ADD_AUTHORITY + REGISTER_CREDENTIAL" : "VALIDATE + ADD_AUTHORITY"
391
+ });
392
+ return { ...result, newAuthorityIdx };
393
+ }
394
+
304
395
  // src/auth/use-passkey-auth.ts
305
- var import_passkey_manager3 = require("@thru/passkey-manager");
396
+ var import_passkey_manager4 = require("@thru/programs/passkey-manager");
306
397
  var import_zustand = require("zustand");
307
398
 
308
399
  // src/mobile/errors.ts
@@ -429,18 +520,20 @@ function createPasskeyAuthStore(config) {
429
520
  set({ isLoading: true, error: null, needsNewPasskey: false });
430
521
  try {
431
522
  const tempId = `user-${Date.now()}`;
432
- const { credentialId, publicKeyX, publicKeyY, rpId } = await registerPasskey(resolvedAlias, tempId, {
523
+ const passkeyLabel = resolvedAlias.trim() || "Thru Wallet";
524
+ const { credentialId, publicKeyX, publicKeyY, rpId } = await registerPasskey(passkeyLabel, tempId, {
433
525
  rpId: config.rpId,
434
526
  rpName: config.rpName
435
527
  });
436
528
  const now = (/* @__PURE__ */ new Date()).toISOString();
437
- const pubkeyXHex = (0, import_passkey_manager3.bytesToHex)(publicKeyX);
438
- const pubkeyYHex = (0, import_passkey_manager3.bytesToHex)(publicKeyY);
529
+ const pubkeyXHex = (0, import_passkey_manager4.bytesToHex)(publicKeyX);
530
+ const pubkeyYHex = (0, import_passkey_manager4.bytesToHex)(publicKeyY);
439
531
  await storePasskeyMetadata({
440
532
  credentialId,
441
533
  publicKeyX: pubkeyXHex,
442
534
  publicKeyY: pubkeyYHex,
443
535
  rpId,
536
+ label: passkeyLabel,
444
537
  createdAt: now,
445
538
  lastUsedAt: now
446
539
  });
@@ -664,6 +757,7 @@ function usePasskeyAuth(config) {
664
757
  }
665
758
  // Annotate the CommonJS export names for ESM import in node:
666
759
  0 && (module.exports = {
760
+ addDeviceToAccount,
667
761
  createPasskeyAuthStore,
668
762
  executePasskeyTransaction,
669
763
  getPasskeyAuthStore,