passkey-kit 0.4.1 → 0.4.2

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.2",
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,40 @@
1
+ import { Networks, SorobanRpc, xdr } from "@stellar/stellar-sdk"
2
+ import { Client as FactoryClient } from 'passkey-factory-sdk'
3
+ import base64url from "base64url"
4
+
1
5
  export class PasskeyBase {
6
+ public rpc: SorobanRpc.Server
7
+ public factory: FactoryClient
8
+ public rpcUrl: string
9
+ public networkPassphrase: Networks
2
10
  public launchtubeUrl: string | undefined
3
11
  public launchtubeJwt: string | undefined
12
+ public mercuryUrl: string | undefined
13
+ public mercuryJwt: string | undefined
14
+ public mercuryEmail: string | undefined
15
+ public mercuryPassword: string | undefined
4
16
 
5
17
  constructor(options: {
18
+ rpcUrl: string,
19
+ networkPassphrase: string,
20
+ factoryContractId: string,
6
21
  launchtubeUrl?: string,
7
22
  launchtubeJwt?: string,
23
+ mercuryUrl?: string,
24
+ mercuryJwt?: string,
25
+ mercuryEmail?: string,
26
+ mercuryPassword?: string
8
27
  }) {
9
28
  const {
29
+ rpcUrl,
30
+ networkPassphrase,
31
+ factoryContractId,
10
32
  launchtubeUrl,
11
33
  launchtubeJwt,
34
+ mercuryUrl,
35
+ mercuryJwt,
36
+ mercuryEmail,
37
+ mercuryPassword
12
38
  } = options
13
39
 
14
40
  if (launchtubeUrl)
@@ -16,8 +42,139 @@ export class PasskeyBase {
16
42
 
17
43
  if (launchtubeJwt)
18
44
  this.launchtubeJwt = launchtubeJwt
45
+
46
+ if (mercuryUrl)
47
+ this.mercuryUrl = mercuryUrl
48
+
49
+ if (mercuryJwt)
50
+ this.mercuryJwt = mercuryJwt
51
+
52
+ if (mercuryEmail)
53
+ this.mercuryEmail = mercuryEmail
54
+
55
+ if (mercuryPassword)
56
+ this.mercuryPassword = mercuryPassword
57
+
58
+ this.rpcUrl = rpcUrl
59
+ this.rpc = new SorobanRpc.Server(rpcUrl)
60
+ this.networkPassphrase = networkPassphrase as Networks
61
+ this.factory = new FactoryClient({
62
+ contractId: factoryContractId,
63
+ networkPassphrase,
64
+ rpcUrl
65
+ })
19
66
  }
20
67
 
68
+ public async getMercuryJwt() {
69
+ if (!this.mercuryEmail || !this.mercuryPassword)
70
+ throw new Error('Mercury service not configured')
71
+
72
+ const { data: { authenticate: { jwtToken } } } = await fetch(`${this.mercuryUrl}/graphql`, {
73
+ method: 'POST',
74
+ headers: {
75
+ 'Content-Type': 'application/json',
76
+ },
77
+ body: JSON.stringify({
78
+ query: `mutation {
79
+ authenticate(input: {
80
+ email: "${this.mercuryEmail}"
81
+ password: "${this.mercuryPassword}"
82
+ }) {
83
+ jwtToken
84
+ }
85
+ }`
86
+ })
87
+ })
88
+ .then(async (res) => {
89
+ if (res.ok)
90
+ return res.json()
91
+
92
+ throw await res.json()
93
+ })
94
+
95
+ return jwtToken
96
+ }
97
+
98
+ public async getSigners(contractId: string = this.factory.options.contractId) {
99
+ if (!this.mercuryUrl || !this.mercuryJwt)
100
+ throw new Error('Mercury service not configured')
101
+
102
+ const signers = await fetch(`${this.mercuryUrl}/zephyr/execute`, {
103
+ method: 'POST',
104
+ headers: {
105
+ 'Content-Type': 'application/json',
106
+ Authorization: `Bearer ${this.mercuryJwt}`
107
+ },
108
+ body: JSON.stringify({
109
+ mode: {
110
+ Function: {
111
+ fname: "get_signers_by_address",
112
+ arguments: JSON.stringify({
113
+ address: contractId
114
+ })
115
+ }
116
+ }
117
+ })
118
+ })
119
+ .then(async (res) => {
120
+ if (res.ok)
121
+ return res.json()
122
+
123
+ throw await res.json()
124
+ })
125
+
126
+ for (const signer of signers) {
127
+ if (!signer.admin) {
128
+ try {
129
+ await this.rpc.getContractData(contractId, xdr.ScVal.scvBytes(signer.id), SorobanRpc.Durability.Temporary)
130
+ } catch {
131
+ signer.expired = true
132
+ }
133
+ }
134
+
135
+ signer.id = base64url(signer.id)
136
+ signer.pk = base64url(signer.pk)
137
+ }
138
+
139
+ return signers as { id: string, pk: string, admin: boolean, expired?: boolean }[]
140
+ }
141
+
142
+ public async getContractId(keyId: string) {
143
+ if (!this.mercuryUrl || !this.mercuryJwt)
144
+ return
145
+
146
+ const res = await fetch(`${this.mercuryUrl}/zephyr/execute`, {
147
+ method: 'POST',
148
+ headers: {
149
+ 'Content-Type': 'application/json',
150
+ Authorization: `Bearer ${this.mercuryJwt}`
151
+ },
152
+ body: JSON.stringify({
153
+ mode: {
154
+ Function: {
155
+ fname: "get_address_by_signer",
156
+ arguments: JSON.stringify({
157
+ id: [...base64url.toBuffer(keyId)]
158
+ })
159
+ }
160
+ }
161
+ })
162
+ })
163
+ .then(async (res) => {
164
+ if (res.ok)
165
+ return res.json()
166
+
167
+ throw await res.json()
168
+ })
169
+
170
+ return res[0]?.address as string | undefined
171
+ }
172
+
173
+ /* TODO
174
+ - Add a method for getting a paginated or filtered list of all a wallet's events
175
+ @Later
176
+ */
177
+
21
178
  public async send(xdr: string, fee: number = 10_000) {
22
179
  if (!this.launchtubeUrl || !this.launchtubeJwt)
23
180
  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
- import { Client as FactoryClient } from 'passkey-factory-sdk'
3
- import { Address, Networks, StrKey, hash, xdr, Transaction, SorobanRpc, Operation, TransactionBuilder } from '@stellar/stellar-sdk'
2
+
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,44 +8,22 @@ 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
13
  public wallet: PasskeyClient | undefined
16
- public factory: FactoryClient
17
- public networkPassphrase: Networks
18
- public rpcUrl: string
19
- public rpc: SorobanRpc.Server
20
14
 
21
15
  constructor(options: {
22
16
  rpcUrl: string,
23
- launchtubeUrl?: string,
24
- launchtubeJwt?: string,
25
17
  networkPassphrase: string,
26
18
  factoryContractId: string,
19
+ launchtubeUrl?: string,
20
+ launchtubeJwt?: string,
21
+ mercuryUrl?: string,
22
+ mercuryJwt?: string,
23
+ mercuryEmail?: string,
24
+ mercuryPassword?: string
27
25
  }) {
28
- const {
29
- rpcUrl,
30
- launchtubeUrl,
31
- launchtubeJwt,
32
- networkPassphrase,
33
- factoryContractId,
34
- } = options
35
-
36
- super({
37
- launchtubeUrl,
38
- launchtubeJwt,
39
- })
40
-
41
- this.rpcUrl = rpcUrl
42
- this.rpc = new SorobanRpc.Server(rpcUrl)
43
- this.networkPassphrase = networkPassphrase as Networks
44
- this.factory = new FactoryClient({
45
- contractId: factoryContractId,
46
- networkPassphrase,
47
- rpcUrl
48
- })
26
+ super(options)
49
27
  }
50
28
 
51
29
  public async createWallet(app: string, user: string) {
@@ -75,7 +53,7 @@ export class PasskeyKit extends PasskeyBase {
75
53
  public async createKey(app: string, user: string) {
76
54
  const now = new Date()
77
55
  const displayName = `${user} — ${now.toLocaleString()}`
78
- const { id, response} = await startRegistration({
56
+ const { id, response } = await startRegistration({
79
57
  challenge: base64url("stellaristhebetterblockchain"),
80
58
  rp: {
81
59
  // id: undefined,
@@ -93,6 +71,7 @@ export class PasskeyKit extends PasskeyBase {
93
71
  },
94
72
  pubKeyCredParams: [{ alg: -7, type: "public-key" }],
95
73
  attestation: "none",
74
+ timeout: 120_000
96
75
  });
97
76
 
98
77
  if (!this.keyId)
@@ -104,11 +83,11 @@ export class PasskeyKit extends PasskeyBase {
104
83
  }
105
84
  }
106
85
 
107
- public async connectWallet(opts: {
86
+ public async connectWallet(opts?: {
108
87
  keyId?: string | Uint8Array,
109
- getContractId?: GetContractIdFunction
88
+ getContractId?: (keyId: string) => Promise<string | undefined>
110
89
  }) {
111
- let { keyId, getContractId } = opts
90
+ let { keyId, getContractId = this.getContractId } = opts || {}
112
91
  let keyIdBuffer: Buffer
113
92
 
114
93
  if (!keyId) {
@@ -116,6 +95,7 @@ export class PasskeyKit extends PasskeyBase {
116
95
  challenge: base64url("stellaristhebetterblockchain"),
117
96
  // rpId: undefined,
118
97
  userVerification: "discouraged",
98
+ timeout: 120_000
119
99
  });
120
100
 
121
101
  console.log(response);
@@ -151,12 +131,9 @@ export class PasskeyKit extends PasskeyBase {
151
131
  // TODO what is the error if the entry exists but is archived?
152
132
  await this.rpc.getContractData(contractId, xdr.ScVal.scvLedgerKeyContractInstance())
153
133
  }
154
- // if that fails look up from the factory mapper
134
+ // if that fails look up from the `getContractId` function
155
135
  catch {
156
- contractId = undefined
157
-
158
- if (getContractId)
159
- contractId = await getContractId(keyId)
136
+ contractId = await getContractId(keyId)
160
137
  }
161
138
 
162
139
  if (!contractId)
@@ -203,6 +180,7 @@ export class PasskeyKit extends PasskeyBase {
203
180
  challenge: base64url(payload),
204
181
  // rpId: undefined,
205
182
  userVerification: "discouraged",
183
+ timeout: 120_000
206
184
  }
207
185
  : {
208
186
  challenge: base64url(payload),
@@ -216,6 +194,7 @@ export class PasskeyKit extends PasskeyBase {
216
194
  },
217
195
  ],
218
196
  userVerification: "discouraged",
197
+ timeout: 120_000
219
198
  }
220
199
  );
221
200
 
package/types/base.d.ts CHANGED
@@ -1,9 +1,34 @@
1
+ import { Networks, SorobanRpc } from "@stellar/stellar-sdk";
2
+ import { Client as FactoryClient } from 'passkey-factory-sdk';
1
3
  export declare class PasskeyBase {
4
+ rpc: SorobanRpc.Server;
5
+ factory: FactoryClient;
6
+ rpcUrl: string;
7
+ networkPassphrase: Networks;
2
8
  launchtubeUrl: string | undefined;
3
9
  launchtubeJwt: string | undefined;
10
+ mercuryUrl: string | undefined;
11
+ mercuryJwt: string | undefined;
12
+ mercuryEmail: string | undefined;
13
+ mercuryPassword: string | undefined;
4
14
  constructor(options: {
15
+ rpcUrl: string;
16
+ networkPassphrase: string;
17
+ factoryContractId: string;
5
18
  launchtubeUrl?: string;
6
19
  launchtubeJwt?: string;
20
+ mercuryUrl?: string;
21
+ mercuryJwt?: string;
22
+ mercuryEmail?: string;
23
+ mercuryPassword?: string;
7
24
  });
25
+ getMercuryJwt(): Promise<any>;
26
+ getSigners(contractId?: string): Promise<{
27
+ id: string;
28
+ pk: string;
29
+ admin: boolean;
30
+ expired?: boolean;
31
+ }[]>;
32
+ getContractId(keyId: string): Promise<string | undefined>;
8
33
  send(xdr: string, fee?: number): Promise<any>;
9
34
  }
package/types/kit.d.ts CHANGED
@@ -1,22 +1,20 @@
1
1
  import { Client as PasskeyClient } from 'passkey-kit-sdk';
2
- import { Client as FactoryClient } from 'passkey-factory-sdk';
3
- import { Networks, xdr, Transaction, SorobanRpc } from '@stellar/stellar-sdk';
2
+ import { xdr, Transaction } from '@stellar/stellar-sdk';
4
3
  import { Buffer } from 'buffer';
5
4
  import { PasskeyBase } from './base';
6
- type GetContractIdFunction = (keyId: string) => Promise<string>;
7
5
  export declare class PasskeyKit extends PasskeyBase {
8
6
  keyId: string | undefined;
9
7
  wallet: PasskeyClient | undefined;
10
- factory: FactoryClient;
11
- networkPassphrase: Networks;
12
- rpcUrl: string;
13
- rpc: SorobanRpc.Server;
14
8
  constructor(options: {
15
9
  rpcUrl: string;
16
- launchtubeUrl?: string;
17
- launchtubeJwt?: string;
18
10
  networkPassphrase: string;
19
11
  factoryContractId: string;
12
+ launchtubeUrl?: string;
13
+ launchtubeJwt?: string;
14
+ mercuryUrl?: string;
15
+ mercuryJwt?: string;
16
+ mercuryEmail?: string;
17
+ mercuryPassword?: string;
20
18
  });
21
19
  createWallet(app: string, user: string): Promise<{
22
20
  keyId: Buffer;
@@ -27,9 +25,9 @@ export declare class PasskeyKit extends PasskeyBase {
27
25
  keyId: Buffer;
28
26
  publicKey: Buffer;
29
27
  }>;
30
- connectWallet(opts: {
28
+ connectWallet(opts?: {
31
29
  keyId?: string | Uint8Array;
32
- getContractId?: GetContractIdFunction;
30
+ getContractId?: (keyId: string) => Promise<string | undefined>;
33
31
  }): Promise<{
34
32
  keyId: Buffer;
35
33
  contractId: string;
@@ -49,4 +47,3 @@ export declare class PasskeyKit extends PasskeyBase {
49
47
  private getPublicKey;
50
48
  private compactSignature;
51
49
  }
52
- export {};