From 090263dc9d7eb69d7c8c62e5744bbfc50b929cf3 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Fri, 15 Dec 2023 07:55:27 -0300 Subject: [PATCH] feat(auth): add auth callback to client --- Cargo.toml | 2 +- src/client.rs | 32 +++++++++++++---- src/core/auth.rs | 68 +++++++++++++++++++++++++++++++++++-- src/core/withdraw.rs | 2 +- src/core/withdraw/client.rs | 2 +- src/core/withdraw/server.rs | 2 +- 6 files changed, 95 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9398ea8..9df229b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ readme = "README.md" [dependencies] base64 = { version = "0.21.0", features = ["std"], default-features = false } bech32 = { version = "0.9.0", default-features = false } -hex = { version = "0.4.3", features = ["serde"], default-features = false } +hex = { version = "0.4.3", features = ["std", "serde"], default-features = false } serde = { version = "1.0.0", features = ["derive"], default-features = false } serde_json = { version = "1.0.0", features = ["std"], default-features = false } serde_urlencoded = { version = "0.7.0", default-features = false } diff --git a/src/client.rs b/src/client.rs index 1f150f1..7851323 100644 --- a/src/client.rs +++ b/src/client.rs @@ -10,12 +10,7 @@ impl Client { let url = match crate::resolve(s)? { crate::Resolved::Url(url) => url, - crate::Resolved::Auth(_, core) => { - return Ok(Entrypoint::Auth(Auth { - _client: client, - core, - })) - } + crate::Resolved::Auth(_, core) => return Ok(Entrypoint::Auth(Auth { client, core })), crate::Resolved::Withdraw(_, core) => { return Ok(Entrypoint::Withdraw(Withdraw { client, core })) } @@ -47,7 +42,7 @@ pub enum Entrypoint<'a> { #[derive(Clone, Debug)] pub struct Auth<'a> { - _client: &'a reqwest::Client, + client: &'a reqwest::Client, pub core: crate::auth::Entrypoint, } @@ -69,6 +64,29 @@ pub struct Withdraw<'a> { pub core: crate::withdraw::client::Entrypoint, } +impl Auth<'_> { + /// # Errors + /// + /// Returns errors on network or deserialization failures. + pub async fn auth( + &self, + key: &str, + sig: &[u8; 64], + ) -> Result { + let callback = self.core.auth(key, sig); + + let response = self + .client + .get(callback.to_string()) + .send() + .await + .map_err(|_| "request failed")?; + + let bytes = response.bytes().await.map_err(|_| "body failed")?; + (&bytes as &[u8]).try_into().map_err(|_| "parse failed") + } +} + impl Channel<'_> { /// # Errors /// diff --git a/src/core/auth.rs b/src/core/auth.rs index 0734e4a..131b04e 100644 --- a/src/core/auth.rs +++ b/src/core/auth.rs @@ -36,11 +36,55 @@ impl TryFrom<&str> for Entrypoint { } } +impl Entrypoint { + #[must_use] + pub fn auth<'a>(&'a self, key: &'a str, sig: &'a [u8; 64]) -> Callback<'a> { + Callback { + url: &self.url, + k1: &self.k1, + key, + sig, + } + } +} + +pub struct Callback<'a> { + pub url: &'a url::Url, + pub k1: &'a [u8; 32], + pub sig: &'a [u8; 64], + pub key: &'a str, +} + +impl std::fmt::Display for Callback<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let query = ser::Callback { + sig: self.sig, + key: self.key, + }; + + let querystr = serde_urlencoded::to_string(query).map_err(|_| std::fmt::Error)?; + let sep = if self.url.query().is_some() { '&' } else { '?' }; + write!(f, "{}{sep}{querystr}", self.url) + } +} + +mod ser { + use serde::Serialize; + + #[derive(Serialize)] + pub(super) struct Callback<'a> { + #[serde(with = "hex::serde")] + pub sig: &'a [u8; 64], + #[serde(with = "hex::serde")] + pub key: &'a str, + } +} + mod de { use serde::Deserialize; #[derive(Deserialize)] - pub struct Entrypoint { + pub(super) struct Entrypoint { #[serde(with = "hex::serde")] pub k1: [u8; 32], pub action: Option, @@ -48,7 +92,7 @@ mod de { #[derive(Deserialize)] #[serde(rename_all = "lowercase")] - pub enum Action { + pub(super) enum Action { Register, Login, Link, @@ -91,4 +135,24 @@ mod tests { let parsed: super::Entrypoint = input.try_into().expect("try_into"); assert!(matches!(parsed.action, Some(super::Action::Auth))); } + + #[test] + fn callback_render() { + let input = + "https://site.com?k1=6f697072617a65726575736f756f6261697465732176616d6f63616d69676f73"; + + let parsed: super::Entrypoint = input.try_into().expect("try_into"); + assert_eq!( + parsed + .auth( + "chaves", + b"0123456789012345678901234567890123456789012345678901234567890123" + ) + .to_string(), + "https://site.com/\ + ?k1=6f697072617a65726575736f756f6261697465732176616d6f63616d69676f73\ + &sig=30313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233\ + &key=636861766573" + ); + } } diff --git a/src/core/withdraw.rs b/src/core/withdraw.rs index 3c43134..dedef99 100644 --- a/src/core/withdraw.rs +++ b/src/core/withdraw.rs @@ -6,7 +6,7 @@ mod serde { use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize)] - pub(super) struct CallbackQuery<'a> { + pub(super) struct Callback<'a> { pub k1: &'a str, pub pr: &'a str, } diff --git a/src/core/withdraw/client.rs b/src/core/withdraw/client.rs index 21ad30e..1a578d1 100644 --- a/src/core/withdraw/client.rs +++ b/src/core/withdraw/client.rs @@ -62,7 +62,7 @@ pub struct Callback<'a> { impl std::fmt::Display for Callback<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let query = super::serde::CallbackQuery { + let query = super::serde::Callback { k1: self.k1, pr: self.pr, }; diff --git a/src/core/withdraw/server.rs b/src/core/withdraw/server.rs index f5e99c1..d097bdc 100644 --- a/src/core/withdraw/server.rs +++ b/src/core/withdraw/server.rs @@ -47,7 +47,7 @@ impl<'a> TryFrom<&'a str> for Callback { type Error = &'static str; fn try_from(s: &'a str) -> Result { - serde_urlencoded::from_str::(s) + serde_urlencoded::from_str::(s) .map_err(|_| "deserialize failed") .map(|query| Callback { k1: String::from(query.k1),