From 8276205c44d8c20ff6aa35b526c781ce0007387d Mon Sep 17 00:00:00 2001 From: Patrice Vignola Date: Sat, 11 Mar 2023 16:33:10 -0800 Subject: [PATCH] Add session-id cmd option (#55) --- README.md | 13 ++++++++- include/arg_parser.hpp | 4 +++ include/auth_helpers.hpp | 17 ++++++++++++ src/CMakeLists.txt | 1 + src/arg_parser.cpp | 27 +++++++++++++++++++ src/arg_parser_test.cpp | 11 ++++++++ src/auth_helpers.cpp | 32 ++++++++++++++++++++++ src/complete_flow.cpp | 58 +++++++++++++++++++++------------------- src/internet_flow.cpp | 50 +++++++++++++++++----------------- 9 files changed, 158 insertions(+), 55 deletions(-) create mode 100644 include/auth_helpers.hpp create mode 100644 src/auth_helpers.cpp diff --git a/README.md b/README.md index 51c35e9..a0552e6 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ More command-line options are also available: C++ KZG Ceremony Client Usage: - ./build/bin/cpp-kzg-ceremony-client [OPTION...] + ./cpp-kzg-ceremony-client [OPTION...] -s, --sequencer arg URL of the sequencer to use (default: https://seq.ceremony.ethereum.org) @@ -90,6 +90,17 @@ Usage: -p, --port arg Port to run the authentication server on. If not provided, a random port will be chosen. + --sessionid arg Manually specify the session id to use for + authentication. The session ID can be + generated on a machine that has browser + support, and then manually entered here for + machines without a browser. + --nickname arg Manually specify the nickname to use for + authentication. For Ethereum + authentication, the address is required; + for GitHub authentication, the GitHub + username is required instead. This option + is required when --sessionid is specified. -h, --help Print usage ``` diff --git a/include/arg_parser.hpp b/include/arg_parser.hpp index af7978b..eb93856 100644 --- a/include/arg_parser.hpp +++ b/include/arg_parser.hpp @@ -34,6 +34,8 @@ class ArgParser { std::vector get_entropy() const; const absl::optional& get_port() const { return port_; } ClientMode get_client_mode() const { return client_mode_; } + absl::string_view get_session_id() const { return session_id_; } + absl::string_view get_nickname() const { return nickname_; } const std::string& get_contribution_file_path() const { return contribution_file_path_; @@ -49,6 +51,8 @@ class ArgParser { EntropyType entropy_type_; absl::optional port_; ClientMode client_mode_; + std::string session_id_; + std::string nickname_; }; #endif // ARG_PARSER_HPP \ No newline at end of file diff --git a/include/auth_helpers.hpp b/include/auth_helpers.hpp new file mode 100644 index 0000000..035583c --- /dev/null +++ b/include/auth_helpers.hpp @@ -0,0 +1,17 @@ +#ifndef AUTH_HELPERS_HPP +#define AUTH_HELPERS_HPP + +#include "include/arg_parser.hpp" +#include "include/auth_info.hpp" +#include "sequencer_client.hpp" +#include + +class SequencerClient; + +namespace auth_helpers { +AuthInfo authenticate(const SequencerClient& sequencer_client, + AuthProvider auth_provider, + std::promise& auth_info_promise); +} // namespace auth_helpers + +#endif // AUTH_HELPERS_HPP \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bdec949..86a5ed0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ add_library( airgapped_flow.cpp arg_parser.cpp auth_browser.cpp + auth_helpers.cpp auth_request_link.cpp batch_contribution.cpp batch_transcript.cpp diff --git a/src/arg_parser.cpp b/src/arg_parser.cpp index e440cc1..a0f1153 100644 --- a/src/arg_parser.cpp +++ b/src/arg_parser.cpp @@ -57,6 +57,21 @@ ArgParser::ArgParser(int argc, const char* const* argv) { "provided, a random port will be chosen.", cxxopts::value(), ""); + options.add_option( + "", "", "sessionid", + "Manually specify the session id to use for authentication. The " + "session ID can be generated on a machine that has browser support, " + "and then manually entered here for machines without a browser.", + cxxopts::value(), ""); + + options.add_option( + "", "", "nickname", + "Manually specify the nickname to use for authentication. For Ethereum " + "authentication, the address is required; for GitHub authentication, " + "the GitHub username is required instead. This option is required when " + "--sessionid is specified.", + cxxopts::value(), ""); + options.add_option("", "h", "help", "Print usage", cxxopts::value(), ""); @@ -119,6 +134,18 @@ ArgParser::ArgParser(int argc, const char* const* argv) { if (parse_result.count("port") > 0) { port_ = parse_result["port"].as(); } + + if (parse_result.count("sessionid") > 0) { + session_id_ = parse_result["sessionid"].as(); + + if (parse_result.count("nickname") > 0) { + nickname_ = parse_result["nickname"].as(); + } else { + throw std::runtime_error( + "--nickname is required when --sessionid is specified"); + } + } + } catch (const cxxopts::option_not_exists_exception& ex) { throw std::runtime_error(absl::StrCat( "error when parsing arguments: ", ex.what(), "\n", help_message_)); diff --git a/src/arg_parser_test.cpp b/src/arg_parser_test.cpp index 4369d8d..bbd4deb 100644 --- a/src/arg_parser_test.cpp +++ b/src/arg_parser_test.cpp @@ -42,6 +42,17 @@ static constexpr const char* usage_message = R"( -p, --port arg Port to run the authentication server on. If not provided, a random port will be chosen. + --sessionid arg Manually specify the session id to use for + authentication. The session ID can be + generated on a machine that has browser + support, and then manually entered here for + machines without a browser. + --nickname arg Manually specify the nickname to use for + authentication. For Ethereum + authentication, the address is required; + for GitHub authentication, the GitHub + username is required instead. This option + is required when --sessionid is specified. -h, --help Print usage )"; diff --git a/src/auth_helpers.cpp b/src/auth_helpers.cpp new file mode 100644 index 0000000..496cbcb --- /dev/null +++ b/src/auth_helpers.cpp @@ -0,0 +1,32 @@ +#include "include/auth_helpers.hpp" +#include "include/auth_browser.hpp" +#include "include/sequencer_client.hpp" + +namespace auth_helpers { +AuthInfo authenticate(const SequencerClient& sequencer_client, + AuthProvider auth_provider, + std::promise& auth_info_promise) { + const auto auth_request_link = sequencer_client.get_auth_request_link(); + + const auto auth_url = [&auth_provider, &auth_request_link]() { + switch (auth_provider) { + case AuthProvider::Ethereum: + return auth_request_link.get_eth_auth_url(); + case AuthProvider::GitHub: + return auth_request_link.get_github_auth_url(); + default: + throw std::runtime_error("Error: unsupported authentication provider"); + } + }(); + + AuthBrowser auth_browser((std::string(auth_url))); + auto auth_future = auth_info_promise.get_future(); + AuthInfo auth_info = auth_future.get(); + + if (!auth_info.get_error_message().empty()) { + throw std::runtime_error(auth_info.get_error_message()); + } + + return auth_info; +} +} // namespace auth_helpers \ No newline at end of file diff --git a/src/complete_flow.cpp b/src/complete_flow.cpp index 0833edc..023ec32 100644 --- a/src/complete_flow.cpp +++ b/src/complete_flow.cpp @@ -1,6 +1,7 @@ #include "include/arg_parser.hpp" #include "include/ascii_title.hpp" #include "include/auth_browser.hpp" +#include "include/auth_helpers.hpp" #include "include/auth_info.hpp" #include "include/auth_request_link.hpp" #include "include/bls_signature.hpp" @@ -79,36 +80,30 @@ void launch(const ArgParser& arg_parser) { bool contribution_successful = false; while (!contribution_successful) { - const auto auth_request_link = sequencer_client.get_auth_request_link(); + absl::optional auth_info; + auto session_id = arg_parser.get_session_id(); + auto nickname = arg_parser.get_nickname(); - const auto auth_url = [&auth_provider, &auth_request_link]() { - switch (auth_provider) { - case AuthProvider::Ethereum: - return auth_request_link.get_eth_auth_url(); - case AuthProvider::GitHub: - return auth_request_link.get_github_auth_url(); - default: - throw std::runtime_error("Error: unsupported authentication provider"); - } - }(); + if (session_id.empty()) { + auth_info.emplace(auth_helpers::authenticate( + sequencer_client, auth_provider, auth_info_promise)); - AuthBrowser auth_browser((std::string(auth_url))); - auto auth_future = auth_info_promise.get_future(); - AuthInfo auth_info = auth_future.get(); + session_id = auth_info->get_session_id(); + nickname = auth_info->get_nickname(); - if (!auth_info.get_error_message().empty()) { - throw std::runtime_error(auth_info.get_error_message()); + std::cout << "Authentication successful!" << std::endl; + std::cout << "sessionid: " << session_id << std::endl; + std::cout << "nickname: " << nickname << std::endl; } // Retrieve the identity (e.g. eth|0xa7fb...) std::cout << "Retrieving your identity" << std::endl; - const auto identity = [&auth_provider, &auth_info]() { + const auto identity = [auth_provider, nickname]() { switch (auth_provider) { case AuthProvider::Ethereum: - return identity_fetcher::get_ethereum_identity( - auth_info.get_nickname()); + return identity_fetcher::get_ethereum_identity(nickname); case AuthProvider::GitHub: - return identity_fetcher::get_github_identity(auth_info.get_nickname()); + return identity_fetcher::get_github_identity(nickname); default: throw std::runtime_error("Error: unsupported authentication provider"); } @@ -117,9 +112,8 @@ void launch(const ArgParser& arg_parser) { std::string ecdsa_signature; if (auth_provider == AuthProvider::Ethereum && !arg_parser.signing_disabled() && ecdsa_signature.empty()) { - const auto signing_url = - absl::StrCat("http://localhost:", port, - "/sign?eth_address=", auth_info.get_nickname()); + const auto signing_url = absl::StrCat("http://localhost:", port, + "/sign?eth_address=", nickname); SigningBrowser signing_browser(signing_url); auto ecdsa_signature_future = ecdsa_signature_promise.get_future(); @@ -140,8 +134,7 @@ void launch(const ArgParser& arg_parser) { try { // Wait until a contribution slot is available - auto batch_contribution = - sequencer_client.try_contribute(auth_info.get_session_id()); + auto batch_contribution = sequencer_client.try_contribute(session_id); // Validate the powers batch_contribution.validate_powers(); @@ -164,8 +157,8 @@ void launch(const ArgParser& arg_parser) { } std::cout << "Submitting the updated contributions" << std::endl; - const auto contribution_receipt = sequencer_client.contribute( - auth_info.get_session_id(), batch_contribution); + const auto contribution_receipt = + sequencer_client.contribute(session_id, batch_contribution); std::cout << "Your contribution was successfully submitted! Here is " "your contribution receipt:" @@ -177,8 +170,17 @@ void launch(const ArgParser& arg_parser) { contribution_successful = true; } catch (const UnknownSessionIdError& ex) { - std::cout << "Session ID expired. Try authenticating again." << std::endl; + auto error_message = + absl::StrCat("Session ID `", session_id, + "` is invalid or expired. Try authenticating again."); + + if (!arg_parser.get_session_id().empty()) { + throw std::invalid_argument(error_message); + } + + std::cout << error_message << std::endl; auth_info_promise = std::promise(); + ecdsa_signature_promise = std::promise(); } } } diff --git a/src/internet_flow.cpp b/src/internet_flow.cpp index 6bde5bf..9c53db1 100644 --- a/src/internet_flow.cpp +++ b/src/internet_flow.cpp @@ -1,6 +1,7 @@ #include "include/arg_parser.hpp" #include "include/ascii_title.hpp" #include "include/auth_browser.hpp" +#include "include/auth_helpers.hpp" #include "include/auth_info.hpp" #include "include/auth_request_link.hpp" #include "include/bls_signature.hpp" @@ -46,36 +47,26 @@ void launch(const ArgParser& arg_parser) { bool contribution_successful = false; while (!contribution_successful) { - const auto auth_request_link = sequencer_client.get_auth_request_link(); + absl::optional auth_info; + auto session_id = arg_parser.get_session_id(); + auto nickname = arg_parser.get_nickname(); - const auto auth_url = [&auth_provider, &auth_request_link]() { - switch (auth_provider) { - case AuthProvider::Ethereum: - return auth_request_link.get_eth_auth_url(); - case AuthProvider::GitHub: - return auth_request_link.get_github_auth_url(); - default: - throw std::runtime_error("Error: unsupported authentication provider"); - } - }(); - - AuthBrowser auth_browser((std::string(auth_url))); - auto auth_future = auth_info_promise.get_future(); - AuthInfo auth_info = auth_future.get(); + if (session_id.empty()) { + auth_info.emplace(auth_helpers::authenticate( + sequencer_client, auth_provider, auth_info_promise)); - if (!auth_info.get_error_message().empty()) { - throw std::runtime_error(auth_info.get_error_message()); + session_id = auth_info->get_session_id(); + nickname = auth_info->get_nickname(); } // Retrieve the identity (e.g. eth|0xa7fb...) std::cout << "Retrieving your identity" << std::endl; - const auto identity = [&auth_provider, &auth_info]() { + const auto identity = [auth_provider, nickname]() { switch (auth_provider) { case AuthProvider::Ethereum: - return identity_fetcher::get_ethereum_identity( - auth_info.get_nickname()); + return identity_fetcher::get_ethereum_identity(nickname); case AuthProvider::GitHub: - return identity_fetcher::get_github_identity(auth_info.get_nickname()); + return identity_fetcher::get_github_identity(nickname); default: throw std::runtime_error("Error: unsupported authentication provider"); } @@ -83,8 +74,7 @@ void launch(const ArgParser& arg_parser) { try { // Wait until a contribution slot is available - auto batch_contribution = - sequencer_client.try_contribute(auth_info.get_session_id()); + auto batch_contribution = sequencer_client.try_contribute(session_id); // Validate the powers batch_contribution.validate_powers(); @@ -137,8 +127,8 @@ void launch(const ArgParser& arg_parser) { batch_contribution.validate_powers(); std::cout << "Submitting the updated contributions" << std::endl; - const auto contribution_receipt = sequencer_client.contribute( - auth_info.get_session_id(), batch_contribution); + const auto contribution_receipt = + sequencer_client.contribute(session_id, batch_contribution); std::cout << "Your contribution was successfully submitted! Here is " "your contribution receipt:" @@ -150,7 +140,15 @@ void launch(const ArgParser& arg_parser) { contribution_successful = true; } catch (const UnknownSessionIdError& ex) { - std::cout << "Session ID expired. Try authenticating again." << std::endl; + auto error_message = + absl::StrCat("Session ID `", session_id, + "` is invalid or expired. Try authenticating again."); + + if (!arg_parser.get_session_id().empty()) { + throw std::invalid_argument(error_message); + } + + std::cout << error_message << std::endl; auth_info_promise = std::promise(); } }