Skip to content

[Design] Automatically lock wallet due to inactivity #353

Open
@alexruzenhack

Description

@alexruzenhack

Author: Alex Ruzenhack [email protected]
Reviewers: André Carneiro, Pedro Ferreira

Motivation

Add an extra level of security for the user by protecting the wallet in case of inactivity, avoiding a malicious agent to operate the wallet in the absence of its owner.

Introduction

What inactivity means for our purpose? A person can be using the computer but not using the wallet; a person can be out of the computer and leave the wallet open; a person can have the wallet open as the primary window, but just looking the screen without interacting with the wallet.

"Activity" presumes the action of interacting with the wallet, if the interaction ceases then the wallet is not being used and it can be marked as inactive. However, how many times do we need to mark the wallet as inactive?

The time span of no interaction that characterizes inactivity in the wallet has an inverse relation with the protection of the wallet. We can say the smaller the time span, the more protected the wallet will be. On the other hand, the time span also has a direct relation with convenience. The smaller the time span, the smaller the convenience for the user.

The time span to lock the wallet should provide a new layer of protection for the user at their discretion of the desired convenience.

Proposed solution

Taking activity by interaction, we should monitor each interaction with the wallet: click, touch, roll, type, tab navigation; establish an elapsed time for the last interaction and verify periodically if the elapsed time has reached the time span threshold for inactivity, then lock the wallet if reached.

Guide-level explanation

Global interaction event listener

We can set an event listener for each targeted interaction in the application element level, which means any event bubbling up will be captured if not halted by stopPropagation. Every time an event is processed the interaction time is updated.

Last interaction state

We should create a state called lastInteractionAt to hold the timestamp of the last interaction with the wallet in Redux.

Inactivity threshold state

We should create a state called inactivityThreshold to hold the time in seconds that characterizes the inactivity in the wallet. This state can be populated with a default value of 300 seconds (5min).

Lock function

A function to lock the wallet is to be run as a callback for setTimeout, which should set timeout as inactivityThreshold value. For every interaction, the timeout is canceled and set again with the current inactivityThreshold value.

Hardware wallet lock

The lock function in this context will redirect the user to the screen wallet type selection.

Jitter protection

To avoid flooding the event processing a jitter must be placed in the event listeners. Or maybe not, to avoid over-engineering.

Reference-level Explanation

Global interaction state

We can add an event listener in the Root element by the component reference and use the hook useEffect to add the listener and program its removal once the component is unmounted.

Last interaction state

We can initialize the state lastInteractionAt in the initial state of Redux with the value of the timestamp in UTC as new Date().getTime().

const initialState = {
	// last user interaction with the wallet
	lastInteractionAt: new Date().getTime(),
	...
}

Inactivity threshold state

We can initialize the state inactivityThreshold with a default value that can be a configured constant as DEFAULT_INACTIVITY_THRESHOLD with the value of 300 seconds.

const initialState = {
	// last user interaction with the wallet
	inactivityThreshold: DEFAULT_INACTIVITY_THRESHOLD,
	...
}

Lock function

Software wallet

Should call the lock function from HathorLib.

Hardware wallet

Should navigate to the page "wallet type selection".

Interaction listener

The interaction listener should update lastInteractionAt state and set the timeout for the lock function. However, for the lock function, it must be set only if the wallet status is ready.

Hardware wallet

When the wallet is used with a device like Ledger, the wallet also listens to the device's events, therefore these events also need to be hooked to update lastInteractionAt and schedule or reschedule the lock function.

Close event

When the Ledger device is inactive with Hathor App for 30 sec, an event of ledger:closed (see ledger.js:124) is emitted in the electron. This event is captured on a listener defined at App.js (see App.js:81), which updates the state ledgerWasClosed.

  • What is the meaning of ledgerWasClosed: true for the wallet renderization state? (see WalletType.js:92)
  • Does it impact on redirection to "wallet type selection"?

Assess the impact of Multiple Wallets PR

See feat: Add support for multiple wallets.

Software wallet

When the wallet is loaded the function hathorLib.wallet.markWalletAsStarted() is called. Therefore there is no real change here because we already consider triggering the setTimeout with the lock function once the wallet is ready/started.

See LoadWallet.js:104.

Hardware wallet

When initializing the wallet, in the handlePublicKeyData logic there is a call to hathorLib.wallet.unlock(), we may use it to trigger the first interaction with the wallet and set the setTimeout with the lock function.

See StartHardwareWallet.js:129.

When adding a wallet

In this situation, the setTimout for the lock function should be canceled, to avoid a lock call in the middle of a new wallet load.

See ChooseWallet.js:69(addWallet).

When going to hardware wallet

As this type of wallet if more ephemeral for the application, the setTimeout in this situation also needs to be canceled.

See ChooseWallet.js:79(goToHardwareWallet).

Task Breakdown

  • Implement a global event handler to capture user interaction with the wallet, update lasInteractionAt state, and schedule/reschedule the lock function. 1 dev day
  • Implement a selector for options of inactive threshold values in the settings page, and update the state inactivityThreshold . 1 dev day
  • Add documentation for feature usage from the user's perspective. 0.5 dev day

Future work

  • Implement reactions for power-monitor events, like capturing the screen lock event to lock the wallet as well without the need to wait the inactivity time threshold.
  • Should we implement the configurable inactiveThreshold? If so, let the user choose a larger time for inactivity depending on battery conditions.

Alternative solutions

If the meaning of “inactivity“ is other than “interaction” we could think of it as “visible activity” and use window information to handle inactivity.

Active window

Native solution through visibility of window

As documented in “Page visibility”, the visibility property is not consistent, therefore it will be tricky to implement any feature relying on it. I think it is better to avoid it.

image

Read at: BrowserWindow | Electron (electronjs.org)

External solution through **electron-active-window package

The project https://github.com/nullxx/electron-active-window has few stars, and downloads, and the last maintenance was 15 months ago. Despite these facts, there is a testimony very interesting about compatibility with macOS and privacy settings.

We should consider this path only if we intend to give maintenance to the package.

image

Read at: Comparison with active-win lib · Issue #3 · nullxx/electron-active-window (github.com)

Metadata

Metadata

Assignees

Labels

designIssue that describes a project in details

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions