Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/wallet/dapp-client/src/ChainSessionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ import {
TransportMode,
GuardConfig,
CreateNewSessionPayload,
EthAuthSettings,
ModifyExplicitSessionPayload,
SessionResponse,
AddExplicitSessionPayload,
FeeOption,
OperationFailedStatus,
OperationStatus,
ETHAuthProof,
} from './types/index.js'
import { CACHE_DB_NAME, VALUE_FORWARDER_ADDRESS } from './utils/constants.js'
import { ExplicitSession, ImplicitSession, ExplicitSessionConfig } from './index.js'
Expand Down Expand Up @@ -285,6 +287,7 @@ export class ChainSessionManager {
preferredLoginMethod?: LoginMethod
email?: string
includeImplicitSession?: boolean
ethAuth?: EthAuthSettings
} = {},
): Promise<void> {
if (this.isInitialized) {
Expand All @@ -311,6 +314,7 @@ export class ChainSessionManager {
origin,
session: completeSession as ExplicitSession | undefined,
includeImplicitSession: options.includeImplicitSession ?? false,
ethAuth: options.ethAuth,
preferredLoginMethod: options.preferredLoginMethod,
email: options.preferredLoginMethod === 'email' ? options.email : undefined,
}
Expand Down Expand Up @@ -377,6 +381,10 @@ export class ChainSessionManager {
this.guard = guard
}

if (payload.ethAuth) {
await this._saveEthAuthProofIfProvided(connectResponse.ethAuthProof)
}

if (this.transport.mode === TransportMode.POPUP) {
this.transport.closeWallet()
}
Expand Down Expand Up @@ -596,6 +604,10 @@ export class ChainSessionManager {
this.userEmail = userEmail ?? null
this.guard = guard
}

if (savedPayload?.ethAuth) {
await this._saveEthAuthProofIfProvided(connectResponse.ethAuthProof)
}
} else if (response.action === RequestActionType.ADD_EXPLICIT_SESSION) {
if (!this.walletAddress || !Address.isEqual(receivedAddress, this.walletAddress)) {
throw new InitializationError('Received an explicit session for a wallet that is not active.')
Expand Down Expand Up @@ -1104,6 +1116,13 @@ export class ChainSessionManager {
await this.sequenceStorage.clearSessionlessConnection()
}

private async _saveEthAuthProofIfProvided(ethAuthProof?: ETHAuthProof): Promise<void> {
if (!ethAuthProof) {
return
}
await this.sequenceStorage.saveEthAuthProof(ethAuthProof)
}

private _getCachedSignedCall(calls: Payload.Call[]): { to: Address.Address; data: Hex.Hex } | null {
if (!this.lastSignedCallCache) {
return null
Expand Down
11 changes: 11 additions & 0 deletions packages/wallet/dapp-client/src/DappClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import {
GetFeeTokensResponse,
GuardConfig,
LoginMethod,
EthAuthSettings,
RandomPrivateKeyFn,
RequestActionType,
ETHAuthProof,
SendWalletTransactionPayload,
SequenceSessionStorage,
SignMessagePayload,
Expand Down Expand Up @@ -407,6 +409,13 @@ export class DappClient {
}
}

/**
* Returns the latest persisted ETHAuth proof, if one has been received from the wallet.
*/
public async getEthAuthProof(): Promise<ETHAuthProof | null> {
return this.sequenceStorage.getEthAuthProof()
}

/**
* Restores a sessionless connection that was previously persisted via {@link disconnect} or a connect flow.
* @returns A promise that resolves to true if a sessionless connection was applied.
Expand Down Expand Up @@ -559,6 +568,7 @@ export class DappClient {
preferredLoginMethod?: LoginMethod
email?: string
includeImplicitSession?: boolean
ethAuth?: EthAuthSettings
} = {},
): Promise<void> {
if (this.isInitialized) {
Expand Down Expand Up @@ -614,6 +624,7 @@ export class DappClient {
preferredLoginMethod?: LoginMethod
email?: string
includeImplicitSession?: boolean
ethAuth?: EthAuthSettings
} = {},
): Promise<void> {
if (!this.isInitialized || !this.hasSessionlessConnection || !this.walletAddress) {
Expand Down
2 changes: 2 additions & 0 deletions packages/wallet/dapp-client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export type {
FeeToken,
FeeOption,
TransportMessage,
EthAuthSettings,
ETHAuthProof,
} from './types/index.js'
export { RequestActionType, TransportMode, MessageType } from './types/index.js'
export {
Expand Down
21 changes: 21 additions & 0 deletions packages/wallet/dapp-client/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,32 @@ export interface GuardConfig {
moduleAddresses: Map<Address.Address, Address.Address>
}

export interface EthAuthSettings {
app?: string
/** expiry number (in seconds) that is used for ETHAuth proof. Default is 1 week in seconds. */
expiry?: number
/** origin hint of the dapp's host opening the wallet. This value will automatically
* be determined and verified for integrity, and can be omitted. */
origin?: string
/** authorizeNonce is an optional number to be passed as ETHAuth's nonce claim for replay protection. **/
nonce?: number
}

export interface ETHAuthProof {
// eip712 typed-data payload for ETHAuth domain as input
typedData: Payload.TypedDataToSign

// signature encoded in an ETHAuth proof string
ewtString: string
}

// --- Payloads for Transport ---

export interface CreateNewSessionPayload {
origin?: string
session?: ExplicitSession
includeImplicitSession?: boolean
ethAuth?: EthAuthSettings
preferredLoginMethod?: LoginMethod
email?: string
}
Expand Down Expand Up @@ -81,6 +101,7 @@ export interface CreateNewSessionResponse {
userEmail?: string
loginMethod?: LoginMethod
guard?: GuardConfig
ethAuthProof?: ETHAuthProof
}

export interface SignatureResponse {
Expand Down
34 changes: 34 additions & 0 deletions packages/wallet/dapp-client/src/utils/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
SignMessagePayload,
SignTypedDataPayload,
GuardConfig,
ETHAuthProof,
SendWalletTransactionPayload,
ModifyExplicitSessionPayload,
CreateNewSessionPayload,
Expand Down Expand Up @@ -81,6 +82,10 @@ export interface SequenceStorage {
getSessionlessConnection(): Promise<SessionlessConnectionData | null>
clearSessionlessConnection(): Promise<void>

saveEthAuthProof(proof: ETHAuthProof): Promise<void>
getEthAuthProof(): Promise<ETHAuthProof | null>
clearEthAuthProof(): Promise<void>

saveSessionlessConnectionSnapshot?(sessionData: SessionlessConnectionData): Promise<void>
getSessionlessConnectionSnapshot?(): Promise<SessionlessConnectionData | null>
clearSessionlessConnectionSnapshot?(): Promise<void>
Expand All @@ -94,6 +99,7 @@ const STORE_NAME = 'userKeys'
const IMPLICIT_SESSIONS_IDB_KEY = 'SequenceImplicitSession'
const EXPLICIT_SESSIONS_IDB_KEY = 'SequenceExplicitSession'
const SESSIONLESS_CONNECTION_IDB_KEY = 'SequenceSessionlessConnection'
const ETH_AUTH_PROOF_IDB_KEY = 'SequenceEthAuthProof'
const SESSIONLESS_CONNECTION_SNAPSHOT_IDB_KEY = 'SequenceSessionlessConnectionSnapshot'

const PENDING_REDIRECT_REQUEST_KEY = 'SequencePendingRedirect'
Expand Down Expand Up @@ -305,6 +311,15 @@ export class WebStorage implements SequenceStorage {
}
}

async saveEthAuthProof(proof: ETHAuthProof): Promise<void> {
try {
await this.setIDBItem(ETH_AUTH_PROOF_IDB_KEY, proof)
} catch (error) {
console.error('Failed to save ETHAuth proof:', error)
throw error
}
}

async getSessionlessConnection(): Promise<SessionlessConnectionData | null> {
try {
return (await this.getIDBItem<SessionlessConnectionData>(SESSIONLESS_CONNECTION_IDB_KEY)) ?? null
Expand All @@ -314,6 +329,15 @@ export class WebStorage implements SequenceStorage {
}
}

async getEthAuthProof(): Promise<ETHAuthProof | null> {
try {
return (await this.getIDBItem<ETHAuthProof>(ETH_AUTH_PROOF_IDB_KEY)) ?? null
} catch (error) {
console.error('Failed to retrieve ETHAuth proof:', error)
return null
}
}

async clearSessionlessConnection(): Promise<void> {
try {
await this.deleteIDBItem(SESSIONLESS_CONNECTION_IDB_KEY)
Expand All @@ -323,6 +347,15 @@ export class WebStorage implements SequenceStorage {
}
}

async clearEthAuthProof(): Promise<void> {
try {
await this.deleteIDBItem(ETH_AUTH_PROOF_IDB_KEY)
} catch (error) {
console.error('Failed to clear ETHAuth proof:', error)
throw error
}
}

async saveSessionlessConnectionSnapshot(sessionData: SessionlessConnectionData): Promise<void> {
try {
await this.setIDBItem(SESSIONLESS_CONNECTION_SNAPSHOT_IDB_KEY, sessionData)
Expand Down Expand Up @@ -363,6 +396,7 @@ export class WebStorage implements SequenceStorage {
await this.clearExplicitSessions()
await this.clearImplicitSession()
await this.clearSessionlessConnection()
await this.clearEthAuthProof()
await this.clearSessionlessConnectionSnapshot()
} catch (error) {
console.error('Failed to clear all data:', error)
Expand Down
Loading