Skip to content

Commit

Permalink
feat(auth): add auth callback to client
Browse files Browse the repository at this point in the history
  • Loading branch information
lsunsi committed Dec 15, 2023
1 parent 7bf8847 commit 090263d
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
32 changes: 25 additions & 7 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }))
}
Expand Down Expand Up @@ -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,
}

Expand All @@ -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<crate::CallbackResponse, &'static str> {
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
///
Expand Down
68 changes: 66 additions & 2 deletions src/core/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,63 @@ 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<Action>,
}

#[derive(Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Action {
pub(super) enum Action {
Register,
Login,
Link,
Expand Down Expand Up @@ -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"
);
}
}
2 changes: 1 addition & 1 deletion src/core/withdraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/withdraw/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
2 changes: 1 addition & 1 deletion src/core/withdraw/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl<'a> TryFrom<&'a str> for Callback {
type Error = &'static str;

fn try_from(s: &'a str) -> Result<Self, Self::Error> {
serde_urlencoded::from_str::<super::serde::CallbackQuery>(s)
serde_urlencoded::from_str::<super::serde::Callback>(s)
.map_err(|_| "deserialize failed")
.map(|query| Callback {
k1: String::from(query.k1),
Expand Down

0 comments on commit 090263d

Please sign in to comment.