passkey-kit 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/PROPOSAL.md CHANGED
@@ -1,4 +1,4 @@
1
- # Smart Wallet Interface
1
+ # WebAuthn smart wallet contract interface
2
2
 
3
3
  With the release of [Protocol 21](https://stellar.org/blog/developers/announcing-protocol-21) (and specifically the inclusion of the secp256r1 verification curve) Soroban now has tremendous first class support for passkey powered smart wallets.
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "passkey-kit",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "A helper library for creating and using passkey accounts on the Stellar blockchain.",
5
5
  "author": "Tyler van der Hoeven",
6
6
  "license": "MIT",
package/src/base.ts CHANGED
@@ -1,14 +1,35 @@
1
+ import { SorobanRpc, xdr } from "@stellar/stellar-sdk"
2
+ import base64url from "base64url"
3
+
1
4
  export class PasskeyBase {
5
+ public rpc: SorobanRpc.Server
6
+ public rpcUrl: string
2
7
  public launchtubeUrl: string | undefined
3
8
  public launchtubeJwt: string | undefined
9
+ public mercuryUrl: string | undefined
10
+ public mercuryJwt: string | undefined
11
+ public mercuryEmail: string | undefined
12
+ public mercuryPassword: string | undefined
4
13
 
5
14
  constructor(options: {
15
+ rpcUrl: string,
16
+ networkPassphrase: string,
17
+ factoryContractId: string,
6
18
  launchtubeUrl?: string,
7
19
  launchtubeJwt?: string,
20
+ mercuryUrl?: string,
21
+ mercuryJwt?: string,
22
+ mercuryEmail?: string,
23
+ mercuryPassword?: string
8
24
  }) {
9
25
  const {
26
+ rpcUrl,
10
27
  launchtubeUrl,
11
28
  launchtubeJwt,
29
+ mercuryUrl,
30
+ mercuryJwt,
31
+ mercuryEmail,
32
+ mercuryPassword
12
33
  } = options
13
34
 
14
35
  if (launchtubeUrl)
@@ -16,8 +37,137 @@ export class PasskeyBase {
16
37
 
17
38
  if (launchtubeJwt)
18
39
  this.launchtubeJwt = launchtubeJwt
40
+
41
+ if (mercuryUrl)
42
+ this.mercuryUrl = mercuryUrl
43
+
44
+ if (mercuryJwt)
45
+ this.mercuryJwt = mercuryJwt
46
+
47
+ if (mercuryEmail)
48
+ this.mercuryEmail = mercuryEmail
49
+
50
+ if (mercuryPassword)
51
+ this.mercuryPassword = mercuryPassword
52
+
53
+ if (!mercuryJwt && mercuryUrl && mercuryEmail && mercuryPassword)
54
+ this.setMercuryJwt()
55
+
56
+ this.rpcUrl = rpcUrl
57
+ this.rpc = new SorobanRpc.Server(rpcUrl)
19
58
  }
20
59
 
60
+ public async setMercuryJwt() {
61
+ if (!this.mercuryEmail || !this.mercuryPassword)
62
+ throw new Error('Mercury service not configured')
63
+
64
+ const { data: { authenticate: { jwtToken } } } = await fetch(`${this.mercuryUrl}/graphql`, {
65
+ method: 'POST',
66
+ headers: {
67
+ 'Content-Type': 'application/json',
68
+ },
69
+ body: JSON.stringify({
70
+ query: `mutation {
71
+ authenticate(input: {
72
+ email: "${this.mercuryEmail}"
73
+ password: "${this.mercuryPassword}"
74
+ }) {
75
+ jwtToken
76
+ }
77
+ }`
78
+ })
79
+ })
80
+ .then(async (res) => {
81
+ if (res.ok)
82
+ return res.json()
83
+
84
+ throw await res.json()
85
+ })
86
+
87
+ this.mercuryJwt = jwtToken
88
+ return jwtToken
89
+ }
90
+
91
+ public async getSigners(contractId: string) {
92
+ if (!this.mercuryUrl || !this.mercuryJwt)
93
+ throw new Error('Mercury service not configured')
94
+
95
+ const signers = await fetch(`${this.mercuryUrl}/zephyr/execute`, {
96
+ method: 'POST',
97
+ headers: {
98
+ 'Content-Type': 'application/json',
99
+ Authorization: `Bearer ${this.mercuryJwt}`
100
+ },
101
+ body: JSON.stringify({
102
+ mode: {
103
+ Function: {
104
+ fname: "get_signers_by_address",
105
+ arguments: JSON.stringify({
106
+ address: contractId
107
+ })
108
+ }
109
+ }
110
+ })
111
+ })
112
+ .then(async (res) => {
113
+ if (res.ok)
114
+ return res.json()
115
+
116
+ throw await res.json()
117
+ })
118
+
119
+ for (const signer of signers) {
120
+ if (!signer.admin) {
121
+ try {
122
+ await this.rpc.getContractData(contractId, xdr.ScVal.scvBytes(signer.id), SorobanRpc.Durability.Temporary)
123
+ } catch {
124
+ signer.expired = true
125
+ }
126
+ }
127
+
128
+ signer.id = base64url(signer.id)
129
+ signer.pk = base64url(signer.pk)
130
+ }
131
+
132
+ return signers as { id: string, pk: string, admin: boolean, expired?: boolean }[]
133
+ }
134
+
135
+ public async getContractId(keyId: string) {
136
+ if (!this.mercuryUrl || !this.mercuryJwt)
137
+ return
138
+
139
+ const res = await fetch(`${this.mercuryUrl}/zephyr/execute`, {
140
+ method: 'POST',
141
+ headers: {
142
+ 'Content-Type': 'application/json',
143
+ Authorization: `Bearer ${this.mercuryJwt}`
144
+ },
145
+ body: JSON.stringify({
146
+ mode: {
147
+ Function: {
148
+ fname: "get_address_by_signer",
149
+ arguments: JSON.stringify({
150
+ id: [...base64url.toBuffer(keyId)]
151
+ })
152
+ }
153
+ }
154
+ })
155
+ })
156
+ .then(async (res) => {
157
+ if (res.ok)
158
+ return res.json()
159
+
160
+ throw await res.json()
161
+ })
162
+
163
+ return res[0]?.address as string | undefined
164
+ }
165
+
166
+ /* TODO
167
+ - Add a method for getting a paginated or filtered list of all a wallet's events
168
+ @Later
169
+ */
170
+
21
171
  public async send(xdr: string, fee: number = 10_000) {
22
172
  if (!this.launchtubeUrl || !this.launchtubeJwt)
23
173
  throw new Error('Launchtube service not configured')
package/src/kit.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Client as PasskeyClient } from 'passkey-kit-sdk'
2
2
  import { Client as FactoryClient } from 'passkey-factory-sdk'
3
- import { Address, Networks, StrKey, hash, xdr, Transaction, SorobanRpc, Operation, TransactionBuilder } from '@stellar/stellar-sdk'
3
+ import { Address, StrKey, hash, xdr, Transaction, SorobanRpc, Operation, TransactionBuilder } from '@stellar/stellar-sdk'
4
4
  import base64url from 'base64url'
5
5
  import { startRegistration, startAuthentication } from "@simplewebauthn/browser"
6
6
  import type { AuthenticatorAttestationResponseJSON } from "@simplewebauthn/types"
@@ -8,39 +8,28 @@ import { decode } from 'cbor-x/decode'
8
8
  import { Buffer } from 'buffer'
9
9
  import { PasskeyBase } from './base'
10
10
 
11
- type GetContractIdFunction = (keyId: string) => Promise<string>;
12
-
13
11
  export class PasskeyKit extends PasskeyBase {
14
12
  public keyId: string | undefined
15
- public wallet: PasskeyClient | undefined
13
+ public networkPassphrase: string
16
14
  public factory: FactoryClient
17
- public networkPassphrase: Networks
18
- public rpcUrl: string
19
- public rpc: SorobanRpc.Server
15
+ public wallet: PasskeyClient | undefined
20
16
 
21
17
  constructor(options: {
22
18
  rpcUrl: string,
23
- launchtubeUrl?: string,
24
- launchtubeJwt?: string,
25
19
  networkPassphrase: string,
26
20
  factoryContractId: string,
21
+ launchtubeUrl?: string,
22
+ launchtubeJwt?: string,
23
+ mercuryUrl?: string,
24
+ mercuryJwt?: string,
25
+ mercuryEmail?: string,
26
+ mercuryPassword?: string
27
27
  }) {
28
- const {
29
- rpcUrl,
30
- launchtubeUrl,
31
- launchtubeJwt,
32
- networkPassphrase,
33
- factoryContractId,
34
- } = options
28
+ const { rpcUrl, networkPassphrase, factoryContractId } = options
35
29
 
36
- super({
37
- launchtubeUrl,
38
- launchtubeJwt,
39
- })
30
+ super(options)
40
31
 
41
- this.rpcUrl = rpcUrl
42
- this.rpc = new SorobanRpc.Server(rpcUrl)
43
- this.networkPassphrase = networkPassphrase as Networks
32
+ this.networkPassphrase = networkPassphrase
44
33
  this.factory = new FactoryClient({
45
34
  contractId: factoryContractId,
46
35
  networkPassphrase,
@@ -75,7 +64,7 @@ export class PasskeyKit extends PasskeyBase {
75
64
  public async createKey(app: string, user: string) {
76
65
  const now = new Date()
77
66
  const displayName = `${user} — ${now.toLocaleString()}`
78
- const { id, response} = await startRegistration({
67
+ const { id, response } = await startRegistration({
79
68
  challenge: base64url("stellaristhebetterblockchain"),
80
69
  rp: {
81
70
  // id: undefined,
@@ -93,6 +82,7 @@ export class PasskeyKit extends PasskeyBase {
93
82
  },
94
83
  pubKeyCredParams: [{ alg: -7, type: "public-key" }],
95
84
  attestation: "none",
85
+ timeout: 120_000
96
86
  });
97
87
 
98
88
  if (!this.keyId)
@@ -104,11 +94,11 @@ export class PasskeyKit extends PasskeyBase {
104
94
  }
105
95
  }
106
96
 
107
- public async connectWallet(opts: {
97
+ public async connectWallet(opts?: {
108
98
  keyId?: string | Uint8Array,
109
- getContractId?: GetContractIdFunction
99
+ getContractId?: (keyId: string) => Promise<string | undefined>
110
100
  }) {
111
- let { keyId, getContractId } = opts
101
+ let { keyId, getContractId = this.getContractId } = opts || {}
112
102
  let keyIdBuffer: Buffer
113
103
 
114
104
  if (!keyId) {
@@ -116,6 +106,7 @@ export class PasskeyKit extends PasskeyBase {
116
106
  challenge: base64url("stellaristhebetterblockchain"),
117
107
  // rpId: undefined,
118
108
  userVerification: "discouraged",
109
+ timeout: 120_000
119
110
  });
120
111
 
121
112
  console.log(response);
@@ -151,12 +142,9 @@ export class PasskeyKit extends PasskeyBase {
151
142
  // TODO what is the error if the entry exists but is archived?
152
143
  await this.rpc.getContractData(contractId, xdr.ScVal.scvLedgerKeyContractInstance())
153
144
  }
154
- // if that fails look up from the factory mapper
145
+ // if that fails look up from the `getContractId` function
155
146
  catch {
156
- contractId = undefined
157
-
158
- if (getContractId)
159
- contractId = await getContractId(keyId)
147
+ contractId = await getContractId(keyId)
160
148
  }
161
149
 
162
150
  if (!contractId)
@@ -203,6 +191,7 @@ export class PasskeyKit extends PasskeyBase {
203
191
  challenge: base64url(payload),
204
192
  // rpId: undefined,
205
193
  userVerification: "discouraged",
194
+ timeout: 120_000
206
195
  }
207
196
  : {
208
197
  challenge: base64url(payload),
@@ -216,6 +205,7 @@ export class PasskeyKit extends PasskeyBase {
216
205
  },
217
206
  ],
218
207
  userVerification: "discouraged",
208
+ timeout: 120_000
219
209
  }
220
210
  );
221
211
 
package/types/base.d.ts CHANGED
@@ -1,9 +1,31 @@
1
+ import { SorobanRpc } from "@stellar/stellar-sdk";
1
2
  export declare class PasskeyBase {
3
+ rpc: SorobanRpc.Server;
4
+ rpcUrl: string;
2
5
  launchtubeUrl: string | undefined;
3
6
  launchtubeJwt: string | undefined;
7
+ mercuryUrl: string | undefined;
8
+ mercuryJwt: string | undefined;
9
+ mercuryEmail: string | undefined;
10
+ mercuryPassword: string | undefined;
4
11
  constructor(options: {
12
+ rpcUrl: string;
13
+ networkPassphrase: string;
14
+ factoryContractId: string;
5
15
  launchtubeUrl?: string;
6
16
  launchtubeJwt?: string;
17
+ mercuryUrl?: string;
18
+ mercuryJwt?: string;
19
+ mercuryEmail?: string;
20
+ mercuryPassword?: string;
7
21
  });
22
+ setMercuryJwt(): Promise<any>;
23
+ getSigners(contractId: string): Promise<{
24
+ id: string;
25
+ pk: string;
26
+ admin: boolean;
27
+ expired?: boolean;
28
+ }[]>;
29
+ getContractId(keyId: string): Promise<string | undefined>;
8
30
  send(xdr: string, fee?: number): Promise<any>;
9
31
  }
package/types/kit.d.ts CHANGED
@@ -1,22 +1,23 @@
1
1
  import { Client as PasskeyClient } from 'passkey-kit-sdk';
2
2
  import { Client as FactoryClient } from 'passkey-factory-sdk';
3
- import { Networks, xdr, Transaction, SorobanRpc } from '@stellar/stellar-sdk';
3
+ import { xdr, Transaction } from '@stellar/stellar-sdk';
4
4
  import { Buffer } from 'buffer';
5
5
  import { PasskeyBase } from './base';
6
- type GetContractIdFunction = (keyId: string) => Promise<string>;
7
6
  export declare class PasskeyKit extends PasskeyBase {
8
7
  keyId: string | undefined;
9
- wallet: PasskeyClient | undefined;
8
+ networkPassphrase: string;
10
9
  factory: FactoryClient;
11
- networkPassphrase: Networks;
12
- rpcUrl: string;
13
- rpc: SorobanRpc.Server;
10
+ wallet: PasskeyClient | undefined;
14
11
  constructor(options: {
15
12
  rpcUrl: string;
16
- launchtubeUrl?: string;
17
- launchtubeJwt?: string;
18
13
  networkPassphrase: string;
19
14
  factoryContractId: string;
15
+ launchtubeUrl?: string;
16
+ launchtubeJwt?: string;
17
+ mercuryUrl?: string;
18
+ mercuryJwt?: string;
19
+ mercuryEmail?: string;
20
+ mercuryPassword?: string;
20
21
  });
21
22
  createWallet(app: string, user: string): Promise<{
22
23
  keyId: Buffer;
@@ -27,9 +28,9 @@ export declare class PasskeyKit extends PasskeyBase {
27
28
  keyId: Buffer;
28
29
  publicKey: Buffer;
29
30
  }>;
30
- connectWallet(opts: {
31
+ connectWallet(opts?: {
31
32
  keyId?: string | Uint8Array;
32
- getContractId?: GetContractIdFunction;
33
+ getContractId?: (keyId: string) => Promise<string | undefined>;
33
34
  }): Promise<{
34
35
  keyId: Buffer;
35
36
  contractId: string;
@@ -49,4 +50,3 @@ export declare class PasskeyKit extends PasskeyBase {
49
50
  private getPublicKey;
50
51
  private compactSignature;
51
52
  }
52
- export {};