(feat): improvements to the slow
This commit is contained in:
parent
8bc3215ab8
commit
c51c0f2e83
7 changed files with 76 additions and 21 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1888,6 +1888,7 @@ dependencies = [
|
|||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"url",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
name = "tauth-sdk"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
license = "UNLICENSED"
|
||||
license = "MIT"
|
||||
|
||||
[lib]
|
||||
name = "tauth_sdk"
|
||||
|
|
@ -14,6 +14,7 @@ mtp = { git = "https://git.methanium.net/methanium/mtp.git", features = ["client
|
|||
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
sha2 = "0.10"
|
||||
thiserror = "2"
|
||||
tokio = { version = "1", features = ["rt", "time"] }
|
||||
url = "2"
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ let client = TAuthClient::from_config(
|
|||
"https://my.cool.app/tauth/callback",
|
||||
std::env::var("TAUTH_PRIVATE_KEY")?,
|
||||
)
|
||||
.frontend_url("https://tauth.tensamin.net/login"),
|
||||
.frontend_url("https://app.tensamin.net"),
|
||||
)?;
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ Set `TAUTH_PRIVATE_KEY` in `backend/.env`. Generate keys with:
|
|||
pnpm --filter @tauth-example/backend exec cargo run --bin generate_keys
|
||||
```
|
||||
|
||||
Publish the generated `TAUTH_PUBLIC_KEY_SHA256` value in the `tauth.<identifier>` TXT record. TAuth receives the full public key during authorization and verifies it against that hash.
|
||||
|
||||
For local callback testing, `TAUTH_REDIRECT_URL` must be reachable by TAuth. Use a tunnel if needed.
|
||||
|
||||
## Run
|
||||
|
|
|
|||
1
example/backend/Cargo.lock
generated
1
example/backend/Cargo.lock
generated
|
|
@ -1990,6 +1990,7 @@ dependencies = [
|
|||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"url",
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@ fn main() {
|
|||
|
||||
println!("TAUTH_PRIVATE_KEY={}", keys.private);
|
||||
println!("TAUTH_PUBLIC_KEY={}", keys.public);
|
||||
println!("TAUTH_PUBLIC_KEY_SHA256={}", keys.public_hash);
|
||||
}
|
||||
|
|
|
|||
87
src/lib.rs
87
src/lib.rs
|
|
@ -5,10 +5,11 @@ use mtp::crypto::{Keyring, PublicKeyBundle};
|
|||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
const DEFAULT_FRONTEND_URL: &str = "https://tauth.tensamin.net/login";
|
||||
const DEFAULT_FRONTEND_URL: &str = "https://app.tensamin.net";
|
||||
const OMEGA_API: &str = "https://omega.tensamin.net/api";
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
|
@ -39,6 +40,7 @@ pub enum TAuthError {
|
|||
pub struct TAuthKeyPair {
|
||||
pub private: String,
|
||||
pub public: String,
|
||||
pub public_hash: String,
|
||||
}
|
||||
|
||||
pub type TAuthKeys = TAuthKeyPair;
|
||||
|
|
@ -168,7 +170,11 @@ impl TAuthClient {
|
|||
redirect_url: impl AsRef<str>,
|
||||
private_key: impl AsRef<str>,
|
||||
) -> Result<Self, TAuthError> {
|
||||
Self::from_config(TAuthConfig::new(identifier, redirect_url.as_ref(), private_key.as_ref()))
|
||||
Self::from_config(TAuthConfig::new(
|
||||
identifier,
|
||||
redirect_url.as_ref(),
|
||||
private_key.as_ref(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn with_frontend_url(
|
||||
|
|
@ -205,6 +211,10 @@ impl TAuthClient {
|
|||
encode_bytes(&self.public_key_bundle.as_bytes())
|
||||
}
|
||||
|
||||
pub fn public_key_hash(&self) -> String {
|
||||
sha256_hex(self.public_key().as_bytes())
|
||||
}
|
||||
|
||||
pub fn redirect_url(&self) -> &Url {
|
||||
&self.redirect_url
|
||||
}
|
||||
|
|
@ -217,6 +227,7 @@ impl TAuthClient {
|
|||
TAuthKeyPair {
|
||||
private: encode_bytes(&self.keyring.to_bytes()),
|
||||
public: self.public_key(),
|
||||
public_hash: self.public_key_hash(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -238,7 +249,10 @@ impl TAuthClient {
|
|||
link.to_string()
|
||||
}
|
||||
|
||||
pub fn parse_callback(&self, callback_url: impl AsRef<str>) -> Result<AuthCallback, TAuthError> {
|
||||
pub fn parse_callback(
|
||||
&self,
|
||||
callback_url: impl AsRef<str>,
|
||||
) -> Result<AuthCallback, TAuthError> {
|
||||
parse_callback(callback_url)
|
||||
}
|
||||
|
||||
|
|
@ -314,8 +328,13 @@ impl TAuthClient {
|
|||
host_public_key: impl AsRef<str>,
|
||||
) -> Result<MTPConnection, TAuthError> {
|
||||
let omikron = fetch_omikron(user_id).await?;
|
||||
self.connect_or_register_to_omikron(user_id, session_id, omikron.ip_address, host_public_key)
|
||||
.await
|
||||
self.connect_or_register_to_omikron(
|
||||
user_id,
|
||||
session_id,
|
||||
omikron.ip_address,
|
||||
host_public_key,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn connect_or_register_to_omikron(
|
||||
|
|
@ -329,13 +348,15 @@ impl TAuthClient {
|
|||
let config = ClientConfig::new(omikron_url.into())
|
||||
.with_description(format!("tauth:{}:{user_id}", self.identifier))
|
||||
.with_auth_timeout(self.auth_timeout);
|
||||
Ok(MTPClient::auth_connect_or_register(
|
||||
config,
|
||||
session_id,
|
||||
&self.keyring,
|
||||
&host_public_key,
|
||||
Ok(
|
||||
MTPClient::auth_connect_or_register(
|
||||
config,
|
||||
session_id,
|
||||
&self.keyring,
|
||||
&host_public_key,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn connect_session(
|
||||
|
|
@ -359,9 +380,11 @@ impl TAuthClient {
|
|||
|
||||
pub fn generate_key_pair() -> TAuthKeyPair {
|
||||
let keyring = Keyring::generate();
|
||||
let public = encode_bytes(&keyring.public_key_bundle().as_bytes());
|
||||
TAuthKeyPair {
|
||||
private: encode_bytes(&keyring.to_bytes()),
|
||||
public: encode_bytes(&keyring.public_key_bundle().as_bytes()),
|
||||
public_hash: sha256_hex(public.as_bytes()),
|
||||
public,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -440,6 +463,13 @@ fn decode_public_key_bundle(value: &str) -> Result<PublicKeyBundle, TAuthError>
|
|||
.map_err(|error| TAuthError::Key(error.to_string()))
|
||||
}
|
||||
|
||||
fn sha256_hex(bytes: &[u8]) -> String {
|
||||
Sha256::digest(bytes)
|
||||
.iter()
|
||||
.map(|byte| format!("{byte:02x}"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn fetch_omikron(user_id: u64) -> Result<OmikronResponse, TAuthError> {
|
||||
let url = format!("{OMEGA_API}/get/omikron/{user_id}");
|
||||
Ok(reqwest::get(url).await?.error_for_status()?.json().await?)
|
||||
|
|
@ -456,6 +486,7 @@ mod tests {
|
|||
let public = decode_public_key_bundle(&keys.public).unwrap();
|
||||
|
||||
assert_eq!(public.as_bytes(), keyring.public_key_bundle().as_bytes());
|
||||
assert_eq!(keys.public_hash, sha256_hex(keys.public.as_bytes()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -470,9 +501,28 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
let link = Url::parse(&client.generate_link(Some("abc123"))).unwrap();
|
||||
assert_eq!(link.query_pairs().find(|(key, _)| key == "identifier").unwrap().1, "test.app");
|
||||
assert_eq!(link.query_pairs().find(|(key, _)| key == "challenge").unwrap().1, "abc123");
|
||||
assert!(link.query_pairs().any(|(key, value)| key == "public_key" && !value.is_empty()));
|
||||
assert_eq!(
|
||||
link.query_pairs()
|
||||
.find(|(key, _)| key == "identifier")
|
||||
.unwrap()
|
||||
.1,
|
||||
"test.app"
|
||||
);
|
||||
assert_eq!(
|
||||
link.query_pairs()
|
||||
.find(|(key, _)| key == "challenge")
|
||||
.unwrap()
|
||||
.1,
|
||||
"abc123"
|
||||
);
|
||||
assert!(
|
||||
link.query_pairs()
|
||||
.any(|(key, value)| key == "public_key" && !value.is_empty())
|
||||
);
|
||||
assert_eq!(
|
||||
client.public_key_hash(),
|
||||
sha256_hex(client.public_key().as_bytes())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -491,10 +541,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn parses_snake_case_host_key_callback_url() {
|
||||
let callback = parse_callback(
|
||||
"https://app.example/callback?userId=42&sessionId=7&host_public_key=c",
|
||||
)
|
||||
.unwrap();
|
||||
let callback =
|
||||
parse_callback("https://app.example/callback?userId=42&sessionId=7&host_public_key=c")
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(callback.host_public_key.as_deref(), Some("c"));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue