-
Notifications
You must be signed in to change notification settings - Fork 3
Register recovery agent from the signup service #349
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
Changes from 8 commits
c2d9427
ede2a44
d40893d
179ef39
f17530f
dd8ef28
0365933
b8c36ea
8f8e7de
13d8b4c
26a9650
2ecc7ee
7ffd951
b5105b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| //! `walletkit recovery-agent` subcommands — recovery agent management. | ||
|
|
||
| use clap::Subcommand; | ||
|
|
||
| use super::{init_authenticator, Cli}; | ||
| use walletkit_core::issuers::RecoveryBindingManager; | ||
| use walletkit_core::Environment; | ||
|
|
||
| #[derive(Subcommand)] | ||
| pub enum RecoveryBindingCommand { | ||
| /// Register bindings for a recovery agent. | ||
| RegisterBindings { | ||
| leaf_index: u64, | ||
| /// Checksummed hex address of the recovery agent (e.g. "0x1234…"). | ||
| sub: String, | ||
| }, | ||
| UnregisterBindings { | ||
| leaf_index: u64, | ||
| sub: String, | ||
| }, | ||
| } | ||
|
|
||
| pub async fn run( | ||
| cli: &Cli, | ||
| action: &RecoveryBindingCommand, | ||
| environment: &Environment, | ||
| ) -> eyre::Result<()> { | ||
| let (authenticator, _store) = init_authenticator(cli).await?; | ||
|
|
||
| match action { | ||
| RecoveryBindingCommand::RegisterBindings { leaf_index, sub } => { | ||
| let recovery_agent_address = environment.poh_recovery_agent_address(); | ||
| let recovery_binding_manager = | ||
| RecoveryBindingManager::new(environment).unwrap(); | ||
| recovery_binding_manager | ||
| .bind_recovery_agent( | ||
| &authenticator, | ||
| *leaf_index, | ||
| sub.clone(), | ||
| recovery_agent_address.clone(), | ||
| ) | ||
| .await?; | ||
| } | ||
| RecoveryBindingCommand::UnregisterBindings { leaf_index, sub } => { | ||
| let recovery_binding_manager = | ||
| RecoveryBindingManager::new(environment).unwrap(); | ||
| recovery_binding_manager | ||
| .unbind_recovery_agent(&authenticator, *leaf_index, sub.clone()) | ||
| .await?; | ||
| } | ||
| } | ||
| Ok(()) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,8 @@ use crate::issuers::pop_backend_client::ManageRecoveryBindingRequest; | |
| use crate::issuers::PopBackendClient; | ||
| use crate::Environment; | ||
| use alloy_primitives::keccak256; | ||
|
|
||
| use std::string::String; | ||
| const ZERO_ADDRESS: &str = "0x0000000000000000000000000000000000000000"; | ||
Dzejkop marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
gryaele marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /// Client for registering and unregistering recovery agents with the `PoP` backend. | ||
| /// | ||
| /// Each instance is bound to a specific [`Environment`] (staging or production), | ||
|
|
@@ -66,6 +67,7 @@ impl RecoveryBindingManager { | |
| /// * `authenticator` — The authenticator whose signing key authorizes the request. | ||
| /// * `leaf_index` — The authenticator's leaf index in the World ID Merkle tree. | ||
| /// * `sub` — Hex-encoded subject identifier of the recovery agent to register. | ||
| /// * `new_recovery_agent` — The checksummed hex address of the new recovery agent (e.g. `"0x1234…"`). | ||
gryaele marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /// | ||
| /// # Errors | ||
| /// | ||
|
|
@@ -76,9 +78,19 @@ impl RecoveryBindingManager { | |
| authenticator: &Authenticator, | ||
| leaf_index: u64, | ||
|
||
| sub: String, | ||
| recovery_agent_address: String, | ||
| ) -> Result<(), WalletKitError> { | ||
| let challenge = self.pop_backend_client.get_challenge().await?; | ||
| let request = ManageRecoveryBindingRequest { sub, leaf_index }; | ||
| let sig_recovery_update = authenticator | ||
| .danger_sign_initiate_recovery_agent_update(recovery_agent_address.clone()) | ||
| .await?; | ||
| let request = ManageRecoveryBindingRequest { | ||
| sub, | ||
| leaf_index, | ||
| signature: format!("0x{}", hex::encode(sig_recovery_update.signature)), | ||
| nonce: sig_recovery_update.nonce.to_string(), | ||
| recovery_agent: recovery_agent_address.clone(), | ||
| }; | ||
| let security_token = Self::generate_recovery_agent_security_token( | ||
| authenticator, | ||
| &request, | ||
|
|
@@ -109,7 +121,17 @@ impl RecoveryBindingManager { | |
| leaf_index: u64, | ||
| sub: String, | ||
| ) -> Result<(), WalletKitError> { | ||
| let request = ManageRecoveryBindingRequest { sub, leaf_index }; | ||
| let recovery_agent = ZERO_ADDRESS.to_string(); | ||
| let sig_recovery_update = authenticator | ||
| .danger_sign_initiate_recovery_agent_update(recovery_agent.clone()) | ||
| .await?; | ||
| let request = ManageRecoveryBindingRequest { | ||
| sub, | ||
| leaf_index, | ||
| signature: format!("0x{}", hex::encode(sig_recovery_update.signature)), | ||
| nonce: sig_recovery_update.nonce.to_string(), | ||
| recovery_agent: recovery_agent.clone(), | ||
gryaele marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }; | ||
| let challenge = self.pop_backend_client.get_challenge().await?; | ||
| let security_token = Self::generate_recovery_agent_security_token( | ||
| authenticator, | ||
|
|
@@ -224,6 +246,7 @@ mod tests { | |
|
|
||
| // Mock the recovery binding registration endpoint | ||
| let url_path = "/api/v1/recovery-binding".to_string(); | ||
| let recovery_agent = "0x1000000000000000000000000000000000000000".to_string(); | ||
|
|
||
| let mock = pop_api_server | ||
| .mock("POST", url_path.as_str()) | ||
|
|
@@ -232,9 +255,11 @@ mod tests { | |
| mockito::Matcher::Regex(".*".to_string()), | ||
| ) | ||
| .match_header("X-Auth-Challenge", challenge.as_str()) | ||
| .match_body(mockito::Matcher::Json(serde_json::json!({ | ||
| .match_body(mockito::Matcher::PartialJson(serde_json::json!({ | ||
| "sub": sub.as_str(), | ||
| "leafIndex": leaf_index, | ||
| "recoveryAgent": recovery_agent.as_str(), | ||
|
|
||
| }))) | ||
| .with_status(201) | ||
| .with_body("{}") | ||
|
|
@@ -254,7 +279,12 @@ mod tests { | |
| create_test_authenticator(&private_key_bytes, rpc_url).await; | ||
|
|
||
| let result = recovery_binding_manager | ||
| .bind_recovery_agent(&authenticator, leaf_index, sub.clone()) | ||
| .bind_recovery_agent( | ||
| &authenticator, | ||
| leaf_index, | ||
| sub.clone(), | ||
| recovery_agent.clone(), | ||
| ) | ||
| .await; | ||
| assert!( | ||
| result.is_ok(), | ||
|
|
@@ -285,10 +315,15 @@ mod tests { | |
| .unwrap(); | ||
| log::info!("message_bytes: {:?}", hex::encode(message_bytes.clone())); | ||
| assert_eq!(hex::encode(message_bytes.clone()), "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2000000000000002aabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"); | ||
|
|
||
| let signature = "0x01".to_string(); | ||
| let nonce = "0x02".to_string(); | ||
| let recovery_agent = "0x1000000000000000000000000000000000000000".to_string(); | ||
| let request = ManageRecoveryBindingRequest { | ||
| sub: sub.clone(), | ||
| leaf_index, | ||
| signature: signature.clone(), | ||
| nonce: nonce.clone(), | ||
| recovery_agent: recovery_agent.clone(), | ||
| }; | ||
| let (mock_eth_server, eth_mock) = create_mock_eth_server().await; | ||
| let rpc_url = mock_eth_server.url(); | ||
|
|
@@ -354,6 +389,8 @@ mod tests { | |
| }) | ||
| .to_string(), | ||
| ) | ||
| .expect_at_least(1) | ||
| .expect_at_most(2) | ||
| .create_async() | ||
| .await; | ||
| (mock_eth_server, mock) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.