Skip to content

Commit e2fc5a4

Browse files
feat: add ethauth option for dapp client connect (#963)
* feat: add ethauth option for dapp client connect * Update tests
1 parent 49d8a2f commit e2fc5a4

6 files changed

Lines changed: 295 additions & 0 deletions

File tree

packages/wallet/dapp-client/src/ChainSessionManager.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ import {
3737
TransportMode,
3838
GuardConfig,
3939
CreateNewSessionPayload,
40+
EthAuthSettings,
4041
ModifyExplicitSessionPayload,
4142
SessionResponse,
4243
AddExplicitSessionPayload,
4344
FeeOption,
4445
OperationFailedStatus,
4546
OperationStatus,
47+
ETHAuthProof,
4648
} from './types/index.js'
4749
import { CACHE_DB_NAME, VALUE_FORWARDER_ADDRESS } from './utils/constants.js'
4850
import { ExplicitSession, ImplicitSession, ExplicitSessionConfig } from './index.js'
@@ -285,6 +287,7 @@ export class ChainSessionManager {
285287
preferredLoginMethod?: LoginMethod
286288
email?: string
287289
includeImplicitSession?: boolean
290+
ethAuth?: EthAuthSettings
288291
} = {},
289292
): Promise<void> {
290293
if (this.isInitialized) {
@@ -311,6 +314,7 @@ export class ChainSessionManager {
311314
origin,
312315
session: completeSession as ExplicitSession | undefined,
313316
includeImplicitSession: options.includeImplicitSession ?? false,
317+
ethAuth: options.ethAuth,
314318
preferredLoginMethod: options.preferredLoginMethod,
315319
email: options.preferredLoginMethod === 'email' ? options.email : undefined,
316320
}
@@ -377,6 +381,10 @@ export class ChainSessionManager {
377381
this.guard = guard
378382
}
379383

384+
if (payload.ethAuth) {
385+
await this._saveEthAuthProofIfProvided(connectResponse.ethAuthProof)
386+
}
387+
380388
if (this.transport.mode === TransportMode.POPUP) {
381389
this.transport.closeWallet()
382390
}
@@ -596,6 +604,10 @@ export class ChainSessionManager {
596604
this.userEmail = userEmail ?? null
597605
this.guard = guard
598606
}
607+
608+
if (savedPayload?.ethAuth) {
609+
await this._saveEthAuthProofIfProvided(connectResponse.ethAuthProof)
610+
}
599611
} else if (response.action === RequestActionType.ADD_EXPLICIT_SESSION) {
600612
if (!this.walletAddress || !Address.isEqual(receivedAddress, this.walletAddress)) {
601613
throw new InitializationError('Received an explicit session for a wallet that is not active.')
@@ -1104,6 +1116,13 @@ export class ChainSessionManager {
11041116
await this.sequenceStorage.clearSessionlessConnection()
11051117
}
11061118

1119+
private async _saveEthAuthProofIfProvided(ethAuthProof?: ETHAuthProof): Promise<void> {
1120+
if (!ethAuthProof) {
1121+
return
1122+
}
1123+
await this.sequenceStorage.saveEthAuthProof(ethAuthProof)
1124+
}
1125+
11071126
private _getCachedSignedCall(calls: Payload.Call[]): { to: Address.Address; data: Hex.Hex } | null {
11081127
if (!this.lastSignedCallCache) {
11091128
return null

packages/wallet/dapp-client/src/DappClient.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import {
1414
GetFeeTokensResponse,
1515
GuardConfig,
1616
LoginMethod,
17+
EthAuthSettings,
1718
RandomPrivateKeyFn,
1819
RequestActionType,
20+
ETHAuthProof,
1921
SendWalletTransactionPayload,
2022
SequenceSessionStorage,
2123
SignMessagePayload,
@@ -407,6 +409,13 @@ export class DappClient {
407409
}
408410
}
409411

412+
/**
413+
* Returns the latest persisted ETHAuth proof, if one has been received from the wallet.
414+
*/
415+
public async getEthAuthProof(): Promise<ETHAuthProof | null> {
416+
return this.sequenceStorage.getEthAuthProof()
417+
}
418+
410419
/**
411420
* Restores a sessionless connection that was previously persisted via {@link disconnect} or a connect flow.
412421
* @returns A promise that resolves to true if a sessionless connection was applied.
@@ -559,6 +568,7 @@ export class DappClient {
559568
preferredLoginMethod?: LoginMethod
560569
email?: string
561570
includeImplicitSession?: boolean
571+
ethAuth?: EthAuthSettings
562572
} = {},
563573
): Promise<void> {
564574
if (this.isInitialized) {
@@ -614,6 +624,7 @@ export class DappClient {
614624
preferredLoginMethod?: LoginMethod
615625
email?: string
616626
includeImplicitSession?: boolean
627+
ethAuth?: EthAuthSettings
617628
} = {},
618629
): Promise<void> {
619630
if (!this.isInitialized || !this.hasSessionlessConnection || !this.walletAddress) {

packages/wallet/dapp-client/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export type {
2424
FeeToken,
2525
FeeOption,
2626
TransportMessage,
27+
EthAuthSettings,
28+
ETHAuthProof,
2729
} from './types/index.js'
2830
export { RequestActionType, TransportMode, MessageType } from './types/index.js'
2931
export {

packages/wallet/dapp-client/src/types/index.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,32 @@ export interface GuardConfig {
2828
moduleAddresses: Map<Address.Address, Address.Address>
2929
}
3030

31+
export interface EthAuthSettings {
32+
app?: string
33+
/** expiry number (in seconds) that is used for ETHAuth proof. Default is 1 week in seconds. */
34+
expiry?: number
35+
/** origin hint of the dapp's host opening the wallet. This value will automatically
36+
* be determined and verified for integrity, and can be omitted. */
37+
origin?: string
38+
/** authorizeNonce is an optional number to be passed as ETHAuth's nonce claim for replay protection. **/
39+
nonce?: number
40+
}
41+
42+
export interface ETHAuthProof {
43+
// eip712 typed-data payload for ETHAuth domain as input
44+
typedData: Payload.TypedDataToSign
45+
46+
// signature encoded in an ETHAuth proof string
47+
ewtString: string
48+
}
49+
3150
// --- Payloads for Transport ---
3251

3352
export interface CreateNewSessionPayload {
3453
origin?: string
3554
session?: ExplicitSession
3655
includeImplicitSession?: boolean
56+
ethAuth?: EthAuthSettings
3757
preferredLoginMethod?: LoginMethod
3858
email?: string
3959
}
@@ -81,6 +101,7 @@ export interface CreateNewSessionResponse {
81101
userEmail?: string
82102
loginMethod?: LoginMethod
83103
guard?: GuardConfig
104+
ethAuthProof?: ETHAuthProof
84105
}
85106

86107
export interface SignatureResponse {

packages/wallet/dapp-client/src/utils/storage.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
SignMessagePayload,
66
SignTypedDataPayload,
77
GuardConfig,
8+
ETHAuthProof,
89
SendWalletTransactionPayload,
910
ModifyExplicitSessionPayload,
1011
CreateNewSessionPayload,
@@ -81,6 +82,10 @@ export interface SequenceStorage {
8182
getSessionlessConnection(): Promise<SessionlessConnectionData | null>
8283
clearSessionlessConnection(): Promise<void>
8384

85+
saveEthAuthProof(proof: ETHAuthProof): Promise<void>
86+
getEthAuthProof(): Promise<ETHAuthProof | null>
87+
clearEthAuthProof(): Promise<void>
88+
8489
saveSessionlessConnectionSnapshot?(sessionData: SessionlessConnectionData): Promise<void>
8590
getSessionlessConnectionSnapshot?(): Promise<SessionlessConnectionData | null>
8691
clearSessionlessConnectionSnapshot?(): Promise<void>
@@ -94,6 +99,7 @@ const STORE_NAME = 'userKeys'
9499
const IMPLICIT_SESSIONS_IDB_KEY = 'SequenceImplicitSession'
95100
const EXPLICIT_SESSIONS_IDB_KEY = 'SequenceExplicitSession'
96101
const SESSIONLESS_CONNECTION_IDB_KEY = 'SequenceSessionlessConnection'
102+
const ETH_AUTH_PROOF_IDB_KEY = 'SequenceEthAuthProof'
97103
const SESSIONLESS_CONNECTION_SNAPSHOT_IDB_KEY = 'SequenceSessionlessConnectionSnapshot'
98104

99105
const PENDING_REDIRECT_REQUEST_KEY = 'SequencePendingRedirect'
@@ -305,6 +311,15 @@ export class WebStorage implements SequenceStorage {
305311
}
306312
}
307313

314+
async saveEthAuthProof(proof: ETHAuthProof): Promise<void> {
315+
try {
316+
await this.setIDBItem(ETH_AUTH_PROOF_IDB_KEY, proof)
317+
} catch (error) {
318+
console.error('Failed to save ETHAuth proof:', error)
319+
throw error
320+
}
321+
}
322+
308323
async getSessionlessConnection(): Promise<SessionlessConnectionData | null> {
309324
try {
310325
return (await this.getIDBItem<SessionlessConnectionData>(SESSIONLESS_CONNECTION_IDB_KEY)) ?? null
@@ -314,6 +329,15 @@ export class WebStorage implements SequenceStorage {
314329
}
315330
}
316331

332+
async getEthAuthProof(): Promise<ETHAuthProof | null> {
333+
try {
334+
return (await this.getIDBItem<ETHAuthProof>(ETH_AUTH_PROOF_IDB_KEY)) ?? null
335+
} catch (error) {
336+
console.error('Failed to retrieve ETHAuth proof:', error)
337+
return null
338+
}
339+
}
340+
317341
async clearSessionlessConnection(): Promise<void> {
318342
try {
319343
await this.deleteIDBItem(SESSIONLESS_CONNECTION_IDB_KEY)
@@ -323,6 +347,15 @@ export class WebStorage implements SequenceStorage {
323347
}
324348
}
325349

350+
async clearEthAuthProof(): Promise<void> {
351+
try {
352+
await this.deleteIDBItem(ETH_AUTH_PROOF_IDB_KEY)
353+
} catch (error) {
354+
console.error('Failed to clear ETHAuth proof:', error)
355+
throw error
356+
}
357+
}
358+
326359
async saveSessionlessConnectionSnapshot(sessionData: SessionlessConnectionData): Promise<void> {
327360
try {
328361
await this.setIDBItem(SESSIONLESS_CONNECTION_SNAPSHOT_IDB_KEY, sessionData)
@@ -363,6 +396,7 @@ export class WebStorage implements SequenceStorage {
363396
await this.clearExplicitSessions()
364397
await this.clearImplicitSession()
365398
await this.clearSessionlessConnection()
399+
await this.clearEthAuthProof()
366400
await this.clearSessionlessConnectionSnapshot()
367401
} catch (error) {
368402
console.error('Failed to clear all data:', error)

0 commit comments

Comments
 (0)