(feat): migrate to rust
This commit is contained in:
parent
788f30eea4
commit
20b12f3c60
28 changed files with 3365 additions and 2775 deletions
244
README.md
244
README.md
|
|
@ -1,74 +1,190 @@
|
|||
# TypeScript TAuth SDK
|
||||
# TAuth SDK
|
||||
|
||||
TypeScript SDK for TAuth-based login and explicit transport session management.
|
||||
Rust library for integrating applications with TAuth and MTP.
|
||||
|
||||
This SDK starts a local HTTP callback server, verifies a challenge, and gives you explicit control over TTP connections per `userId:sessionId`.
|
||||
The SDK handles app key material, TAuth login URLs, callback parsing, Omikron lookup, authenticated MTP connections, and loading/saving user-specific app data.
|
||||
|
||||
## Requirements
|
||||
|
||||
- A modern version of Bun
|
||||
- A domain with TXT record support
|
||||
- App x448 key pair (`privateKey`, `publicKey`) (can be generated)
|
||||
- Rust 1.95+
|
||||
- A TAuth app identifier, usually your app domain
|
||||
- An MTP key pair generated by this library
|
||||
- A public callback URL in your application
|
||||
|
||||
## Install
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tauth-sdk = { git = "https://git.methanium.net/tensamin/tauth-sdk.git" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||
```
|
||||
|
||||
## Generate App Keys
|
||||
|
||||
Run this once, store the private key securely, and publish the public key where TAuth expects app verification data.
|
||||
|
||||
```rust
|
||||
use tauth_sdk::generate_key_pair;
|
||||
|
||||
fn main() {
|
||||
let keys = generate_key_pair();
|
||||
|
||||
println!("private: {}", keys.private);
|
||||
println!("public: {}", keys.public);
|
||||
}
|
||||
```
|
||||
|
||||
Keys are base64-encoded MTP key material. Do not expose the private key.
|
||||
|
||||
## Create A Client
|
||||
|
||||
```rust
|
||||
use tauth_sdk::{TAuthClient, TAuthConfig};
|
||||
|
||||
let client = TAuthClient::from_config(
|
||||
TAuthConfig::new(
|
||||
"my.cool.app",
|
||||
"https://my.cool.app/tauth/callback",
|
||||
std::env::var("TAUTH_PRIVATE_KEY")?,
|
||||
)
|
||||
.frontend_url("https://tauth.tensamin.net/login"),
|
||||
)?;
|
||||
```
|
||||
|
||||
For the default TAuth frontend, this shorter form is equivalent:
|
||||
|
||||
```rust
|
||||
let client = TAuthClient::new(
|
||||
"my.cool.app",
|
||||
"https://my.cool.app/tauth/callback",
|
||||
std::env::var("TAUTH_PRIVATE_KEY")?,
|
||||
)?;
|
||||
```
|
||||
|
||||
## Start Login
|
||||
|
||||
Redirect the user to `client.auth_url(None)` from your web handler.
|
||||
|
||||
```rust
|
||||
let login_url = client.auth_url(None);
|
||||
```
|
||||
|
||||
The URL includes:
|
||||
|
||||
- `identifier`: your app identifier
|
||||
- `redirect`: your callback URL
|
||||
- `public_key`: your app MTP public key bundle
|
||||
- `challenge`: optional caller-supplied challenge
|
||||
|
||||
## Handle Callback
|
||||
|
||||
In your callback route, parse the full callback URL and persist the returned `user_id` and `session_id` for your user.
|
||||
|
||||
```rust
|
||||
let callback = client.parse_callback(full_callback_url)?;
|
||||
|
||||
save_session(callback.user_id, callback.session_id).await?;
|
||||
```
|
||||
|
||||
`AuthCallback` contains:
|
||||
|
||||
```rust
|
||||
pub struct AuthCallback {
|
||||
pub user_id: u64,
|
||||
pub session_id: u64,
|
||||
pub challenge: Option<String>,
|
||||
pub original_challenge: Option<String>,
|
||||
pub host_public_key: Option<String>,
|
||||
}
|
||||
```
|
||||
|
||||
## Connect To TAuth Data Over MTP
|
||||
|
||||
If the callback or your application already has the Omikron host public key, pass it explicitly:
|
||||
|
||||
```rust
|
||||
let session = client
|
||||
.connect_session(callback.user_id, callback.session_id, host_public_key)
|
||||
.await?;
|
||||
```
|
||||
|
||||
If the Omikron API returns a `public_key`, the SDK can look it up automatically:
|
||||
|
||||
```rust
|
||||
let session = client.session_from_callback(&callback).await?;
|
||||
```
|
||||
|
||||
The connection uses MTP authenticated login with your app keyring and the Omikron host public key.
|
||||
|
||||
## Load And Save App Data
|
||||
|
||||
Use serde types for application data:
|
||||
|
||||
```rust
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct AppData {
|
||||
name: String,
|
||||
value: String,
|
||||
}
|
||||
|
||||
let data: AppData = session.load().await?;
|
||||
|
||||
session
|
||||
.save(&AppData {
|
||||
name: "example".into(),
|
||||
value: "updated".into(),
|
||||
})
|
||||
.await?;
|
||||
```
|
||||
|
||||
Raw JSON is also supported:
|
||||
|
||||
```rust
|
||||
let data = session.load_json().await?;
|
||||
session.save_json(&data).await?;
|
||||
```
|
||||
|
||||
## Other Helpers
|
||||
|
||||
Fetch a TAuth user profile:
|
||||
|
||||
```rust
|
||||
let user = client.user(callback.user_id).await?;
|
||||
```
|
||||
|
||||
Connect directly to a known Omikron URL:
|
||||
|
||||
```rust
|
||||
let connection = client
|
||||
.connect_to_omikron(user_id, session_id, "https://omikron.example:4433", host_public_key)
|
||||
.await?;
|
||||
```
|
||||
|
||||
Use lower-level MTP connection helpers when needed:
|
||||
|
||||
```rust
|
||||
let value = tauth_sdk::load_json(&connection).await?;
|
||||
tauth_sdk::save_json(&connection, &value).await?;
|
||||
```
|
||||
|
||||
## MTP Type Map
|
||||
|
||||
This crate compiles an MTP type map for TAuth app data messages:
|
||||
|
||||
- `LoadAppData`
|
||||
- `LoadAppDataResponse`
|
||||
- `SaveAppData`
|
||||
- `SaveAppDataResponse`
|
||||
- `AppData`
|
||||
|
||||
The map lives in `type-maps.yaml` and is wired through `.cargo/config.toml` using `MTP_TYPE_MAPS`.
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
bun add https://git.methanium.net/tensamin/tauth-sdk/archive/0.0.3.tar.gz
|
||||
cargo test
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
## Generate an app key pair
|
||||
|
||||
Use the built-in helper once and store the keys securely:
|
||||
|
||||
```ts
|
||||
import { generateKeyPair } from "@tensamin/tauth-sdk";
|
||||
|
||||
const keys = generateKeyPair();
|
||||
console.log(keys.private);
|
||||
console.log(keys.public);
|
||||
```
|
||||
|
||||
## Add the TXT record
|
||||
|
||||
Add a TXT record at `tauth.your.domain` with your app public key as the value (base64 format). This is used to verify that your app is authorized for your domain.
|
||||
|
||||
## Basic usage
|
||||
|
||||
```ts
|
||||
import z from "zod";
|
||||
import { TAuthClient } from "@tensamin/tauth-sdk";
|
||||
|
||||
const client = new TAuthClient({
|
||||
identifier: "my.cool.app",
|
||||
privateKey: "<APP_PRIVATE_KEY_BASE64>",
|
||||
publicKey: "<APP_PUBLIC_KEY_BASE64>",
|
||||
saveSession: async (userId, sessionId) => {
|
||||
// You must persist the session id yourself (for example in a database).
|
||||
// Treat the session id like a password!
|
||||
console.log("Save session", { userId, sessionId });
|
||||
},
|
||||
redirectUrl: "https://my.cool.app/callback", // This should be a public link proxying http://localhost:7878/callback
|
||||
appData: z.object({
|
||||
my: z.string(),
|
||||
cool: z.string(),
|
||||
data: z.string(),
|
||||
}),
|
||||
httpServer: {
|
||||
hostname: "localhost",
|
||||
port: 7878,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Auth endpoints exposed by the SDK
|
||||
|
||||
- `GET /auth`: Redirects user to TAuth frontend
|
||||
- `GET /callback`: Handles challenge flow and invokes your `saveSession(userId, sessionId)` callback
|
||||
|
||||
## Connection management
|
||||
|
||||
- Use `client.createTTP(userId, sessionId, omikronUrl)` when you want a persistent/manual TTP connection.
|
||||
- Use `client.loadData(userId, sessionId)` for loading user specific application data.
|
||||
- Use `client.saveData(userId, sessionId)` for saving user specific application data.
|
||||
|
||||
These endpoints need to be exposed behind some kind of http proxy to apply ssl
|
||||
|
|
|
|||
Loading…
Reference in a new issue