@0xsequence/guard 1.4.0 → 1.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/dist/0xsequence-guard.cjs.dev.js +324 -68
- package/dist/0xsequence-guard.cjs.prod.js +324 -68
- package/dist/0xsequence-guard.esm.js +322 -65
- package/dist/declarations/src/guard.gen.d.ts +94 -1
- package/dist/declarations/src/index.d.ts +0 -1
- package/dist/declarations/src/signer.d.ts +43 -9
- package/package.json +5 -3
- package/src/guard.gen.ts +179 -4
- package/src/index.ts +0 -1
- package/src/signer.ts +264 -64
package/src/guard.gen.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
|
-
// sequence-guard v0.4.0
|
|
2
|
+
// sequence-guard v0.4.0 2e5d6a4c9b797598078365d7439f330bc7bbf29c
|
|
3
3
|
// --
|
|
4
|
-
// Code generated by webrpc-gen@v0.
|
|
4
|
+
// Code generated by webrpc-gen@v0.12.x-dev with typescript@v0.10.0 generator. DO NOT EDIT.
|
|
5
5
|
//
|
|
6
|
-
// webrpc-gen -schema=guard.ridl -target=typescript -client -out=./clients/guard.gen.ts
|
|
6
|
+
// webrpc-gen -schema=guard.ridl -target=typescript@v0.10.0 -client -out=./clients/guard.gen.ts
|
|
7
7
|
|
|
8
8
|
// WebRPC description and code-gen version
|
|
9
9
|
export const WebRPCVersion = 'v1'
|
|
@@ -12,7 +12,7 @@ export const WebRPCVersion = 'v1'
|
|
|
12
12
|
export const WebRPCSchemaVersion = 'v0.4.0'
|
|
13
13
|
|
|
14
14
|
// Schema hash generated from your RIDL schema
|
|
15
|
-
export const WebRPCSchemaHash = '
|
|
15
|
+
export const WebRPCSchemaHash = '2e5d6a4c9b797598078365d7439f330bc7bbf29c'
|
|
16
16
|
|
|
17
17
|
//
|
|
18
18
|
// Types
|
|
@@ -50,6 +50,23 @@ export interface SignRequest {
|
|
|
50
50
|
auxData: string
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
export interface OwnershipProof {
|
|
54
|
+
wallet: string
|
|
55
|
+
timestamp: number
|
|
56
|
+
signer: string
|
|
57
|
+
signature: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface AuthToken {
|
|
61
|
+
id: string
|
|
62
|
+
token: string
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface RecoveryCode {
|
|
66
|
+
code: string
|
|
67
|
+
used: boolean
|
|
68
|
+
}
|
|
69
|
+
|
|
53
70
|
export interface Guard {
|
|
54
71
|
ping(headers?: object): Promise<PingReturn>
|
|
55
72
|
version(headers?: object): Promise<VersionReturn>
|
|
@@ -57,6 +74,15 @@ export interface Guard {
|
|
|
57
74
|
getSignerConfig(args: GetSignerConfigArgs, headers?: object): Promise<GetSignerConfigReturn>
|
|
58
75
|
sign(args: SignArgs, headers?: object): Promise<SignReturn>
|
|
59
76
|
signWith(args: SignWithArgs, headers?: object): Promise<SignWithReturn>
|
|
77
|
+
authMethods(args: AuthMethodsArgs, headers?: object): Promise<AuthMethodsReturn>
|
|
78
|
+
setPIN(args: SetPINArgs, headers?: object): Promise<SetPINReturn>
|
|
79
|
+
resetPIN(args: ResetPINArgs, headers?: object): Promise<ResetPINReturn>
|
|
80
|
+
createTOTP(args: CreateTOTPArgs, headers?: object): Promise<CreateTOTPReturn>
|
|
81
|
+
commitTOTP(args: CommitTOTPArgs, headers?: object): Promise<CommitTOTPReturn>
|
|
82
|
+
resetTOTP(args: ResetTOTPArgs, headers?: object): Promise<ResetTOTPReturn>
|
|
83
|
+
reset2FA(args: Reset2FAArgs, headers?: object): Promise<Reset2FAReturn>
|
|
84
|
+
recoveryCodes(args: RecoveryCodesArgs, headers?: object): Promise<RecoveryCodesReturn>
|
|
85
|
+
resetRecoveryCodes(args: ResetRecoveryCodesArgs, headers?: object): Promise<ResetRecoveryCodesReturn>
|
|
60
86
|
}
|
|
61
87
|
|
|
62
88
|
export interface PingArgs {}
|
|
@@ -83,6 +109,7 @@ export interface GetSignerConfigReturn {
|
|
|
83
109
|
}
|
|
84
110
|
export interface SignArgs {
|
|
85
111
|
request: SignRequest
|
|
112
|
+
token?: AuthToken
|
|
86
113
|
}
|
|
87
114
|
|
|
88
115
|
export interface SignReturn {
|
|
@@ -91,11 +118,76 @@ export interface SignReturn {
|
|
|
91
118
|
export interface SignWithArgs {
|
|
92
119
|
signer: string
|
|
93
120
|
request: SignRequest
|
|
121
|
+
token?: AuthToken
|
|
94
122
|
}
|
|
95
123
|
|
|
96
124
|
export interface SignWithReturn {
|
|
97
125
|
sig: string
|
|
98
126
|
}
|
|
127
|
+
export interface AuthMethodsArgs {
|
|
128
|
+
proof?: OwnershipProof
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface AuthMethodsReturn {
|
|
132
|
+
methods: Array<string>
|
|
133
|
+
active: boolean
|
|
134
|
+
}
|
|
135
|
+
export interface SetPINArgs {
|
|
136
|
+
pin: string
|
|
137
|
+
timestamp: number
|
|
138
|
+
signature: string
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface SetPINReturn {}
|
|
142
|
+
export interface ResetPINArgs {
|
|
143
|
+
timestamp: number
|
|
144
|
+
signature: string
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface ResetPINReturn {}
|
|
148
|
+
export interface CreateTOTPArgs {
|
|
149
|
+
timestamp: number
|
|
150
|
+
signature: string
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface CreateTOTPReturn {
|
|
154
|
+
uri: string
|
|
155
|
+
}
|
|
156
|
+
export interface CommitTOTPArgs {
|
|
157
|
+
token: string
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface CommitTOTPReturn {
|
|
161
|
+
codes: Array<RecoveryCode>
|
|
162
|
+
}
|
|
163
|
+
export interface ResetTOTPArgs {
|
|
164
|
+
timestamp: number
|
|
165
|
+
signature: string
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface ResetTOTPReturn {}
|
|
169
|
+
export interface Reset2FAArgs {
|
|
170
|
+
code: string
|
|
171
|
+
proof?: OwnershipProof
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface Reset2FAReturn {}
|
|
175
|
+
export interface RecoveryCodesArgs {
|
|
176
|
+
timestamp: number
|
|
177
|
+
signature: string
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface RecoveryCodesReturn {
|
|
181
|
+
codes: Array<RecoveryCode>
|
|
182
|
+
}
|
|
183
|
+
export interface ResetRecoveryCodesArgs {
|
|
184
|
+
timestamp: number
|
|
185
|
+
signature: string
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface ResetRecoveryCodesReturn {
|
|
189
|
+
codes: Array<RecoveryCode>
|
|
190
|
+
}
|
|
99
191
|
|
|
100
192
|
//
|
|
101
193
|
// Client
|
|
@@ -173,6 +265,89 @@ export class Guard implements Guard {
|
|
|
173
265
|
})
|
|
174
266
|
})
|
|
175
267
|
}
|
|
268
|
+
|
|
269
|
+
authMethods = (args: AuthMethodsArgs, headers?: object): Promise<AuthMethodsReturn> => {
|
|
270
|
+
return this.fetch(this.url('AuthMethods'), createHTTPRequest(args, headers)).then(res => {
|
|
271
|
+
return buildResponse(res).then(_data => {
|
|
272
|
+
return {
|
|
273
|
+
methods: <Array<string>>_data.methods,
|
|
274
|
+
active: <boolean>_data.active
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
})
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
setPIN = (args: SetPINArgs, headers?: object): Promise<SetPINReturn> => {
|
|
281
|
+
return this.fetch(this.url('SetPIN'), createHTTPRequest(args, headers)).then(res => {
|
|
282
|
+
return buildResponse(res).then(_data => {
|
|
283
|
+
return {}
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
resetPIN = (args: ResetPINArgs, headers?: object): Promise<ResetPINReturn> => {
|
|
289
|
+
return this.fetch(this.url('ResetPIN'), createHTTPRequest(args, headers)).then(res => {
|
|
290
|
+
return buildResponse(res).then(_data => {
|
|
291
|
+
return {}
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
createTOTP = (args: CreateTOTPArgs, headers?: object): Promise<CreateTOTPReturn> => {
|
|
297
|
+
return this.fetch(this.url('CreateTOTP'), createHTTPRequest(args, headers)).then(res => {
|
|
298
|
+
return buildResponse(res).then(_data => {
|
|
299
|
+
return {
|
|
300
|
+
uri: <string>_data.uri
|
|
301
|
+
}
|
|
302
|
+
})
|
|
303
|
+
})
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
commitTOTP = (args: CommitTOTPArgs, headers?: object): Promise<CommitTOTPReturn> => {
|
|
307
|
+
return this.fetch(this.url('CommitTOTP'), createHTTPRequest(args, headers)).then(res => {
|
|
308
|
+
return buildResponse(res).then(_data => {
|
|
309
|
+
return {
|
|
310
|
+
codes: <Array<RecoveryCode>>_data.codes
|
|
311
|
+
}
|
|
312
|
+
})
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
resetTOTP = (args: ResetTOTPArgs, headers?: object): Promise<ResetTOTPReturn> => {
|
|
317
|
+
return this.fetch(this.url('ResetTOTP'), createHTTPRequest(args, headers)).then(res => {
|
|
318
|
+
return buildResponse(res).then(_data => {
|
|
319
|
+
return {}
|
|
320
|
+
})
|
|
321
|
+
})
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
reset2FA = (args: Reset2FAArgs, headers?: object): Promise<Reset2FAReturn> => {
|
|
325
|
+
return this.fetch(this.url('Reset2FA'), createHTTPRequest(args, headers)).then(res => {
|
|
326
|
+
return buildResponse(res).then(_data => {
|
|
327
|
+
return {}
|
|
328
|
+
})
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
recoveryCodes = (args: RecoveryCodesArgs, headers?: object): Promise<RecoveryCodesReturn> => {
|
|
333
|
+
return this.fetch(this.url('RecoveryCodes'), createHTTPRequest(args, headers)).then(res => {
|
|
334
|
+
return buildResponse(res).then(_data => {
|
|
335
|
+
return {
|
|
336
|
+
codes: <Array<RecoveryCode>>_data.codes
|
|
337
|
+
}
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
resetRecoveryCodes = (args: ResetRecoveryCodesArgs, headers?: object): Promise<ResetRecoveryCodesReturn> => {
|
|
343
|
+
return this.fetch(this.url('ResetRecoveryCodes'), createHTTPRequest(args, headers)).then(res => {
|
|
344
|
+
return buildResponse(res).then(_data => {
|
|
345
|
+
return {
|
|
346
|
+
codes: <Array<RecoveryCode>>_data.codes
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
})
|
|
350
|
+
}
|
|
176
351
|
}
|
|
177
352
|
|
|
178
353
|
export interface WebRPCError extends Error {
|
package/src/index.ts
CHANGED
package/src/signer.ts
CHANGED
|
@@ -1,27 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BytesLike, ethers } from 'ethers'
|
|
3
|
-
import { Guard } from './guard.gen'
|
|
1
|
+
import { Account } from '@0xsequence/account'
|
|
4
2
|
import { commons, universal } from '@0xsequence/core'
|
|
3
|
+
import { signers, Status } from '@0xsequence/signhub'
|
|
4
|
+
import { encodeTypedDataDigest, TypedData } from '@0xsequence/utils'
|
|
5
|
+
import { BytesLike, ethers, TypedDataDomain } from 'ethers'
|
|
6
|
+
import { AuthMethodsReturn, Guard, RecoveryCode as GuardRecoveryCode } from './guard.gen'
|
|
5
7
|
|
|
6
8
|
const fetch = typeof global === 'object' ? global.fetch : window.fetch
|
|
7
9
|
|
|
8
10
|
export class GuardSigner implements signers.SapientSigner {
|
|
9
11
|
private guard: Guard
|
|
10
|
-
private requests: Map<
|
|
11
|
-
string,
|
|
12
|
-
{
|
|
13
|
-
lastAttempt?: string
|
|
14
|
-
onSignature: (signature: BytesLike) => void
|
|
15
|
-
onRejection: (error: string) => void
|
|
16
|
-
onStatus: (situation: string) => void
|
|
17
|
-
}
|
|
18
|
-
> = new Map()
|
|
19
12
|
|
|
20
13
|
constructor(
|
|
21
14
|
public readonly address: string,
|
|
22
15
|
public readonly url: string,
|
|
23
|
-
public readonly appendSuffix: boolean = false
|
|
24
|
-
private readonly onError?: (err: Error) => void
|
|
16
|
+
public readonly appendSuffix: boolean = false
|
|
25
17
|
) {
|
|
26
18
|
this.guard = new Guard(url, fetch)
|
|
27
19
|
}
|
|
@@ -46,8 +38,8 @@ export class GuardSigner implements signers.SapientSigner {
|
|
|
46
38
|
}
|
|
47
39
|
|
|
48
40
|
async requestSignature(
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
_id: string,
|
|
42
|
+
message: BytesLike,
|
|
51
43
|
metadata: object,
|
|
52
44
|
callbacks: {
|
|
53
45
|
onSignature: (signature: BytesLike) => void
|
|
@@ -55,78 +47,286 @@ export class GuardSigner implements signers.SapientSigner {
|
|
|
55
47
|
onStatus: (situation: string) => void
|
|
56
48
|
}
|
|
57
49
|
): Promise<boolean> {
|
|
50
|
+
const { onSignature, onRejection } = callbacks
|
|
51
|
+
|
|
58
52
|
if (!commons.isWalletSignRequestMetadata(metadata)) {
|
|
59
|
-
|
|
53
|
+
onRejection('expected sequence signature request metadata')
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const guardTotpCode = (metadata as { guardTotpCode?: string }).guardTotpCode
|
|
58
|
+
|
|
59
|
+
// Building auxData, notice: this uses the old v1 format
|
|
60
|
+
// TODO: We should update the guard API so we can pass the metadata directly
|
|
61
|
+
const coder = universal.genericCoderFor(metadata.config.version)
|
|
62
|
+
const { encoded } = coder.signature.encodeSigners(metadata.config, metadata.parts ?? new Map(), [], metadata.chainId)
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const { sig: signature } = await this.guard.signWith({
|
|
66
|
+
signer: this.address,
|
|
67
|
+
request: {
|
|
68
|
+
msg: ethers.utils.hexlify(message),
|
|
69
|
+
auxData: this.packMsgAndSig(metadata.address, metadata.digest, encoded, metadata.chainId),
|
|
70
|
+
chainId: ethers.BigNumber.from(metadata.chainId).toNumber()
|
|
71
|
+
},
|
|
72
|
+
token: guardTotpCode ? { id: AuthMethod.TOTP, token: guardTotpCode } : undefined
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
if (ethers.utils.arrayify(signature).length === 0) {
|
|
76
|
+
throw new Error('guard response contained no signature data')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
onSignature(signature)
|
|
80
|
+
return true
|
|
81
|
+
} catch (error) {
|
|
82
|
+
onRejection(`unable to request guard signature: ${error.message ?? error.msg ?? error}`)
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
notifyStatusChange(_id: string, _status: Status, _metadata: object): void {}
|
|
88
|
+
|
|
89
|
+
async getAuthMethods(proof: OwnershipProof): Promise<AuthMethod[]> {
|
|
90
|
+
let response: AuthMethodsReturn
|
|
91
|
+
|
|
92
|
+
if ('jwt' in proof) {
|
|
93
|
+
response = await this.guard.authMethods({}, { Authorization: `BEARER ${proof.jwt}` })
|
|
60
94
|
} else {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.
|
|
95
|
+
const signedProof = await signOwnershipProof(proof)
|
|
96
|
+
|
|
97
|
+
response = await this.guard.authMethods({
|
|
98
|
+
proof: {
|
|
99
|
+
wallet: signedProof.walletAddress,
|
|
100
|
+
timestamp: signedProof.timestamp.getTime(),
|
|
101
|
+
signer: signedProof.signerAddress,
|
|
102
|
+
signature: signedProof.signature
|
|
103
|
+
}
|
|
104
|
+
})
|
|
64
105
|
}
|
|
65
106
|
|
|
66
|
-
return
|
|
107
|
+
return response.methods.map(parseAuthMethod)
|
|
67
108
|
}
|
|
68
109
|
|
|
69
|
-
|
|
70
|
-
|
|
110
|
+
async setPin(pin: string | undefined, proof: AuthUpdateProof): Promise<void> {
|
|
111
|
+
const signedProof = await signAuthUpdateProof(proof)
|
|
71
112
|
|
|
72
|
-
if (
|
|
73
|
-
this.
|
|
74
|
-
|
|
113
|
+
if (pin === undefined) {
|
|
114
|
+
await this.guard.resetPIN(
|
|
115
|
+
{ timestamp: signedProof.timestamp.getTime(), signature: signedProof.signature },
|
|
116
|
+
{ Authorization: `BEARER ${proof.jwt}` }
|
|
117
|
+
)
|
|
118
|
+
} else {
|
|
119
|
+
await this.guard.setPIN(
|
|
120
|
+
{ pin, timestamp: signedProof.timestamp.getTime(), signature: signedProof.signature },
|
|
121
|
+
{ Authorization: `BEARER ${proof.jwt}` }
|
|
122
|
+
)
|
|
75
123
|
}
|
|
124
|
+
}
|
|
76
125
|
|
|
77
|
-
|
|
126
|
+
resetPin(proof: AuthUpdateProof): Promise<void> {
|
|
127
|
+
return this.setPin(undefined, proof)
|
|
78
128
|
}
|
|
79
129
|
|
|
80
|
-
|
|
81
|
-
|
|
130
|
+
async createTotp(proof: AuthUpdateProof): Promise<URL> {
|
|
131
|
+
const signedProof = await signAuthUpdateProof(proof)
|
|
132
|
+
|
|
133
|
+
const { uri } = await this.guard.createTOTP(
|
|
134
|
+
{ timestamp: signedProof.timestamp.getTime(), signature: signedProof.signature },
|
|
135
|
+
{ Authorization: `BEARER ${proof.jwt}` }
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return new URL(uri)
|
|
82
139
|
}
|
|
83
140
|
|
|
84
|
-
|
|
85
|
-
|
|
141
|
+
async commitTotp(token: string, jwt: string): Promise<RecoveryCode[]> {
|
|
142
|
+
const { codes } = await this.guard.commitTOTP({ token }, { Authorization: `BEARER ${jwt}` })
|
|
143
|
+
return codes
|
|
86
144
|
}
|
|
87
145
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
message: BytesLike,
|
|
91
|
-
_: Status,
|
|
92
|
-
metadata: commons.WalletSignRequestMetadata
|
|
93
|
-
): Promise<void> {
|
|
94
|
-
// Building auxData, notice: this uses the old v1 format
|
|
95
|
-
// TODO: We should update the guard API so we can pass the metadata directly
|
|
96
|
-
const coder = universal.genericCoderFor(metadata.config.version)
|
|
97
|
-
const { encoded } = coder.signature.encodeSigners(metadata.config, metadata.parts ?? new Map(), [], metadata.chainId)
|
|
146
|
+
async resetTotp(proof: AuthUpdateProof): Promise<void> {
|
|
147
|
+
const signedProof = await signAuthUpdateProof(proof)
|
|
98
148
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
149
|
+
await this.guard.resetTOTP(
|
|
150
|
+
{ timestamp: signedProof.timestamp.getTime(), signature: signedProof.signature },
|
|
151
|
+
{ Authorization: `BEARER ${proof.jwt}` }
|
|
152
|
+
)
|
|
153
|
+
}
|
|
105
154
|
|
|
106
|
-
|
|
155
|
+
async reset2fa(recoveryCode: string, proof: OwnershipProof): Promise<void> {
|
|
156
|
+
if ('jwt' in proof) {
|
|
157
|
+
await this.guard.reset2FA({ code: recoveryCode }, { Authorization: `BEARER ${proof.jwt}` })
|
|
158
|
+
} else {
|
|
159
|
+
const signedProof = await signOwnershipProof(proof)
|
|
107
160
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
161
|
+
await this.guard.reset2FA({
|
|
162
|
+
code: recoveryCode,
|
|
163
|
+
proof: {
|
|
164
|
+
wallet: signedProof.walletAddress,
|
|
165
|
+
timestamp: signedProof.timestamp.getTime(),
|
|
166
|
+
signer: signedProof.signerAddress,
|
|
167
|
+
signature: signedProof.signature
|
|
114
168
|
}
|
|
115
169
|
})
|
|
116
|
-
|
|
117
|
-
if (ethers.utils.arrayify(result.sig).length !== 0) {
|
|
118
|
-
this.requests.get(id)!.onSignature(result.sig)
|
|
119
|
-
this.requests.delete(id)
|
|
120
|
-
}
|
|
121
|
-
} catch (e) {
|
|
122
|
-
// The guard signer may reject the request for a number of reasons
|
|
123
|
-
// like for example, if it's being the first signer (it waits for other signers to sign first)
|
|
124
|
-
// We always forward the error here and filter on client side.
|
|
125
|
-
this.onError?.(e)
|
|
126
170
|
}
|
|
127
171
|
}
|
|
128
172
|
|
|
173
|
+
async getRecoveryCodes(proof: AuthUpdateProof): Promise<RecoveryCode[]> {
|
|
174
|
+
const signedProof = await signAuthUpdateProof(proof)
|
|
175
|
+
|
|
176
|
+
const { codes } = await this.guard.recoveryCodes(
|
|
177
|
+
{ timestamp: signedProof.timestamp.getTime(), signature: signedProof.signature },
|
|
178
|
+
{ Authorization: `BEARER ${proof.jwt}` }
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
return codes
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async resetRecoveryCodes(proof: AuthUpdateProof): Promise<RecoveryCode[]> {
|
|
185
|
+
const signedProof = await signAuthUpdateProof(proof)
|
|
186
|
+
|
|
187
|
+
const { codes } = await this.guard.resetRecoveryCodes(
|
|
188
|
+
{ timestamp: signedProof.timestamp.getTime(), signature: signedProof.signature },
|
|
189
|
+
{ Authorization: `BEARER ${proof.jwt}` }
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
return codes
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private packMsgAndSig(address: string, msg: BytesLike, sig: BytesLike, chainId: ethers.BigNumberish): string {
|
|
196
|
+
return ethers.utils.defaultAbiCoder.encode(['address', 'uint256', 'bytes', 'bytes'], [address, chainId, msg, sig])
|
|
197
|
+
}
|
|
198
|
+
|
|
129
199
|
suffix(): BytesLike {
|
|
130
200
|
return this.appendSuffix ? [3] : []
|
|
131
201
|
}
|
|
132
202
|
}
|
|
203
|
+
|
|
204
|
+
export type RecoveryCode = GuardRecoveryCode
|
|
205
|
+
|
|
206
|
+
export enum AuthMethod {
|
|
207
|
+
PIN = 'PIN',
|
|
208
|
+
TOTP = 'TOTP'
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function parseAuthMethod(method: string): AuthMethod {
|
|
212
|
+
switch (method) {
|
|
213
|
+
case AuthMethod.PIN:
|
|
214
|
+
case AuthMethod.TOTP:
|
|
215
|
+
return method
|
|
216
|
+
default:
|
|
217
|
+
throw new Error(`unknown auth method '${method}'`)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export type OwnershipProof =
|
|
222
|
+
| { jwt: string }
|
|
223
|
+
| {
|
|
224
|
+
walletAddress: string
|
|
225
|
+
timestamp: Date
|
|
226
|
+
signerAddress: string
|
|
227
|
+
signature: string
|
|
228
|
+
}
|
|
229
|
+
| {
|
|
230
|
+
walletAddress: string
|
|
231
|
+
signer: ethers.Signer | signers.SapientSigner
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function isSignedOwnershipProof(
|
|
235
|
+
proof: OwnershipProof
|
|
236
|
+
): proof is { walletAddress: string; timestamp: Date; signerAddress: string; signature: string } {
|
|
237
|
+
return 'signerAddress' in proof && typeof proof.signerAddress === 'string'
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function signOwnershipProof(
|
|
241
|
+
proof: Exclude<OwnershipProof, { jwt: string }>
|
|
242
|
+
): Promise<{ walletAddress: string; timestamp: Date; signerAddress: string; signature: string }> {
|
|
243
|
+
if (isSignedOwnershipProof(proof)) {
|
|
244
|
+
return proof
|
|
245
|
+
} else {
|
|
246
|
+
const signer = signers.isSapientSigner(proof.signer) ? proof.signer : new signers.SignerWrapper(proof.signer)
|
|
247
|
+
const signerAddress = await signer.getAddress()
|
|
248
|
+
const timestamp = new Date()
|
|
249
|
+
const typedData = getOwnershipProofTypedData(proof.walletAddress, timestamp)
|
|
250
|
+
const digest = encodeTypedDataDigest(typedData)
|
|
251
|
+
const randomId = ethers.utils.hexlify(ethers.utils.randomBytes(32))
|
|
252
|
+
|
|
253
|
+
return new Promise((resolve, reject) =>
|
|
254
|
+
signer.requestSignature(
|
|
255
|
+
randomId,
|
|
256
|
+
digest,
|
|
257
|
+
{},
|
|
258
|
+
{
|
|
259
|
+
onSignature(signature) {
|
|
260
|
+
resolve({
|
|
261
|
+
walletAddress: proof.walletAddress,
|
|
262
|
+
timestamp,
|
|
263
|
+
signerAddress,
|
|
264
|
+
signature: ethers.utils.hexlify(signature)
|
|
265
|
+
})
|
|
266
|
+
},
|
|
267
|
+
onRejection: reject,
|
|
268
|
+
onStatus(_situation) {}
|
|
269
|
+
}
|
|
270
|
+
)
|
|
271
|
+
)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export type AuthUpdateProof = { jwt: string } & ({ timestamp: Date; signature: string } | { wallet: Account })
|
|
276
|
+
|
|
277
|
+
async function signAuthUpdateProof(proof: AuthUpdateProof): Promise<{ jwt: string; timestamp: Date; signature: string }> {
|
|
278
|
+
if ('wallet' in proof) {
|
|
279
|
+
const timestamp = new Date()
|
|
280
|
+
const typedData = getAuthUpdateProofTypedData(timestamp)
|
|
281
|
+
|
|
282
|
+
const signature = await proof.wallet.signTypedData(
|
|
283
|
+
typedData.domain,
|
|
284
|
+
typedData.types,
|
|
285
|
+
typedData.message,
|
|
286
|
+
typedData.domain.chainId ?? 1,
|
|
287
|
+
'eip6492'
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
return { jwt: proof.jwt, timestamp, signature }
|
|
291
|
+
} else {
|
|
292
|
+
return proof
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function getOwnershipProofTypedData(wallet: string, timestamp: Date): TypedData {
|
|
297
|
+
return {
|
|
298
|
+
domain,
|
|
299
|
+
types: {
|
|
300
|
+
AuthMethods: [
|
|
301
|
+
{ name: 'wallet', type: 'address' },
|
|
302
|
+
{ name: 'timestamp', type: 'string' }
|
|
303
|
+
]
|
|
304
|
+
},
|
|
305
|
+
message: {
|
|
306
|
+
wallet: ethers.utils.getAddress(wallet),
|
|
307
|
+
timestamp: toUTCString(timestamp)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function getAuthUpdateProofTypedData(timestamp: Date): TypedData {
|
|
313
|
+
return {
|
|
314
|
+
domain,
|
|
315
|
+
types: {
|
|
316
|
+
AuthUpdate: [{ name: 'timestamp', type: 'string' }]
|
|
317
|
+
},
|
|
318
|
+
message: {
|
|
319
|
+
timestamp: toUTCString(timestamp)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const domain: TypedDataDomain = {
|
|
325
|
+
name: 'Sequence Guard',
|
|
326
|
+
version: '1',
|
|
327
|
+
chainId: 1
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function toUTCString(date: Date): string {
|
|
331
|
+
return date.toUTCString().replace('GMT', 'UTC')
|
|
332
|
+
}
|