Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: move authentication outside the Client #718

Open
wants to merge 22 commits into
base: next
Choose a base branch
from

Conversation

tomyrd
Copy link
Collaborator

@tomyrd tomyrd commented Feb 5, 2025

closes #637

This PR refactors authentication out of the Client. The main idea is that now key tracking is responsability of the authenticator and the addition of new keys is done by the user of the client and not by the Client itself. The Client will only have access to the TransactionAuthenticator::get_signature function that will be used to sign transactions when needed. If the keys were not added by the client user to the authenticator then this will fail.

Authenticator

The authenticator now uses a new KeyStore instead of the client's Store to get the secret key. This keystore now is created outside the client and is responsability of the client's user to keep it updated with every new key.

Keystore

The PR adds two implementations for the keystore:

  • FilesystemKeyStore: based on the filesystem. It stores keys as individual files where the name is the public key and the contents are a serialized AuthSecretKey. It's used by the CLI and by the tests.
  • WebKeySTore: based on the browser local storage. It stores keys as entries using the Web Storage API. It's used in the web client.

Other changes

  • The CLI and integration tests were changed to use the new authenticator. They add keys to the authenticator when creating accounts.
  • The retrieval of authentication information was removed from the Store.
  • The web client now also uses the new authenticator. A lot of extra code was removed because keys were dealt differently in the web client (they were cached so that the authenticator could be synchronous).

TODOs:

  • Fix all the integration tests
  • Add account authentication in the CLI
  • Investigate posible alternative implementations for the web client. The current one doesn't work for wasm.
  • Remove TODOs in code.
  • Add documentation.
  • Clean account auth from stores
  • Add top level module documentation

@tomyrd tomyrd changed the title refactor: remove authentication from Client refactor: move authentication outside the Client Feb 6, 2025
@tomyrd tomyrd force-pushed the tomyrd-authentication-refactor branch from 2d0d0d2 to 7e67c79 Compare February 6, 2025 16:24
@tomyrd tomyrd marked this pull request as ready for review February 10, 2025 17:22
@tomyrd tomyrd force-pushed the tomyrd-authentication-refactor branch from dad3b1b to 8b87cef Compare February 10, 2025 17:26
Copy link
Collaborator

@igamigo igamigo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Cool that this results in a net loss of lines of code, too.
I left some minor comments, related to docs and naming mostly. I also wonder if we should generalize the CliAuthenticator into a Keystore struct to which we are implementing TransactionAuthenticator. This is because it seems like it handles more responsibilities than just authenticating, and also we might want to add new functionalities to it in the future (such as listing keys, removing them, encrypting/decrypting them, etc.).

Word::from(
account
.storage()
.get_item(1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this be index 0 for any non-faucet account?

&account_data.auth_secret_key,
overwrite,
)
.add_account(&account_data.account, account_data.account_seed, overwrite)
.await?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine for now but maybe we could add a TODO here to address potential cases where adding the secret key succeeds, but adding the account fails.

Alternatively, maybe in the future we want to have some sort of "keystore" command that adds/removes keys separately from accounts.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created an issue for this

Comment on lines 202 to 204
authenticator
.add_key(AuthSecretKey::RpoFalcon512(key_pair))
.map_err(CliError::Authentication)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

@@ -22,6 +22,8 @@ pub struct CliConfig {
pub rpc: RpcConfig,
/// Path to the sqlite store file.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Not from this PR but while we are at this, let's write this line as follows

Suggested change
/// Path to the sqlite store file.
/// Path to the SQLite store file.

@@ -22,6 +22,8 @@ pub struct CliConfig {
pub rpc: RpcConfig,
/// Path to the sqlite store file.
pub store_filepath: PathBuf,
/// Path to secret keys file.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Would this be better?

Suggested change
/// Path to secret keys file.
/// Path to the directory that contains the secret key files.

@@ -21,6 +21,9 @@ pub enum CliError {
#[error("asset error")]
#[diagnostic(code(cli::asset_error))]
Asset(#[source] AssetError),
#[error("authentication error")]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe I'd generalize this as key store error, since this is not always authentication-related (for instance, it also manages adding keys to the filesystem)

}

impl<R: Rng> ClientAuthenticator<R> {
/// Creates a new instance of the authenticator.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We should mention that this creates the directory if it does not exist (or alternatively we could do it on the doc comments for pub struct ClientAuthenticator<R>.

};
use rand::Rng;

/// An account authenticator that stores keys in the filesystem.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we expand this doc comment mentioning the way that it stores and looks up files? For instance, saying that it's one file per AuthSecretKey, with a filename that corresponds to the pub key for easy lookups when signatures are requested, etc.

use web_sys::wasm_bindgen::JsValue;

#[derive(Debug, Clone)]
pub struct WebAuthenticator<R> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: A brief description of what this authenticator uses to store files would be cool.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I think eventually we probably want to add password encryption to the keystore so we might want to add an issue to discuss this (or maybe it's more in the domain of the wallet itself? cc @dagarcia7)

@bobbinth
Copy link
Contributor

Should we rebase this PR from the latest next? Seems like it may be including some extraneous changes.

@tomyrd
Copy link
Collaborator Author

tomyrd commented Feb 12, 2025

I reviewed the diff again just to be sure but I couldn't find the extraneous changes you mention. There are a lot of changes that were necessary because of the shift in responsibility in public key tracking. Mainly the tests had to be updated so that they have access to the authenticator in case they need to add new keys (which some of them need).

@tomyrd tomyrd force-pushed the tomyrd-authentication-refactor branch from 194f903 to 82a7f31 Compare February 12, 2025 21:18
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: we can get rid of this file now. It used to be how I tested things, but now with more formal integration tests for the web client, this is obsolete 👍

@dagarcia7
Copy link
Collaborator

Looks great! 💯 Thanks for taking care of the web client changes. I have a quick question. Are there any plans to encrypt the Secret Key before it's put in local storage? And subsequently handle the decryption when retrieving it? I was also wondering if local storage was the best way to store the keys on the web side, but I think given the limitation that the get_signature call is still only synchronous, this may be the only way beyond what we were doing before with the call to cache the auth first before making a transaction call. In the future, if get_signature ends up supporting async, you can play around with this and create different types of WebKeyStore that maybe store things differently -- say, a separate IndexedDB store, or chrome extension storage, etc.

@tomyrd
Copy link
Collaborator Author

tomyrd commented Feb 13, 2025

Are there any plans to encrypt the Secret Key before it's put in local storage? And subsequently handle the decryption when retrieving it?

I think in the future we will probably make this upgrades. This PR is meant to be a first PoC to see how we could move the keystore responsability out of the client. To upgrade we would only need to change the KeyStore which would be much easier.

I was also wondering if local storage was the best way to store the keys on the web side, but I think given the limitation that the get_signature call is still only synchronous

Before this PR we used pollster to wait on the async calls as the SqliteStore had also been changed to make it async. We can change the web storage method to an IndexDB in the future if it's better.

@julian-demox julian-demox removed their request for review February 13, 2025 17:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants