What it is · Feature tour · Quickstart · Honest status · Platform
One Supabase auth story, shared by every Smoo AI Rust client. Browser OAuth with PKCE and a localhost callback, email+password for headless environments, refresh-token rotation, the M2M
client_credentialsgrant — and one0600on-disk credential store holding user and machine sessions side by side. Consumed in production by thethCLI. Rust-only today; not yet on crates.io — a git dependency is the install path.
The auth plumbing every Smoo AI Rust client needs identically, in one crate instead of re-implemented per app:
- Sign a human in — browser OAuth (PKCE + localhost callback), or email+password where no browser exists.
- Keep them signed in — refresh-token grant with Supabase's rotation handled, plus a renew-ahead window.
- Sign a machine in — the RFC 6749
client_credentialsgrant againstauth.smoo.ai/token. - Put the credentials somewhere sane — a
0600store holding user and M2M sessions together.
This crate used to carry the design system too, and called itself
SmooAI/ui's successor. That migration was never finished: nothing ever importedsmooai_client_shared::ui, while the real design-system consumers (smooblue, observability-studio) depended onSmooAI/uidirectly. Two copies of the same files is a drift surface, and it had already drifted — this crate spent weeks serving a monogram missing its inner 'S'. The copy is gone.SmooAI/uiowns the design system; this crate is auth.
| Capability | What you get | |
|---|---|---|
| 🔐 | Browser OAuth (PKCE) | Spawns a localhost callback, opens the browser, captures the Supabase session |
| 🔑 | Email + password grant | Headless-friendly login — SSH, CI, Docker, no browser needed |
| ♻️ | Session refresh | refresh_token grant with rotation handling + a renew-ahead window |
| 🤖 | M2M client_credentials |
RFC 6749 service-account grant against auth.smoo.ai/token |
| 💾 | CredentialsStore |
One 0600 on-disk store for user + M2M sessions, side by side |
All snippets below are the actual API, verified against rust/src/.
The CLI login flow: generate a PKCE verifier/challenge, bind a random localhost port, open the browser to Supabase's authorize endpoint, capture the redirect, exchange the code for a session — and hand back Credentials ready to persist:
use smooai_client_shared::auth::{oauth::{login, OAuthConfig}, CredentialsStore};
let http = reqwest::Client::new();
let cfg = OAuthConfig::new("https://abcd1234.supabase.co", anon_key)
.with_provider("google");
let creds = login(&http, &cfg).await?; // opens the browser, waits ≤5 min
CredentialsStore::default_user()?.save(&creds)?; // ~/.smooth/auth/smooai-user.json, mode 0600Prerequisite:
http://localhostmust be in the Supabase project's Redirect URLs allowlist, and PKCE enabled (GoTrue ≥ v2.95 default).
No browser, no PKCE, no redirect-URL config — works over SSH, in CI, in containers. The password is held in memory only, never stored; MFA-enabled accounts fail with the upstream error verbatim:
use smooai_client_shared::auth::password::password_grant;
let creds = password_grant(&http, supabase_url, anon_key, "you@smoo.ai", &password).await?;Supabase rotates refresh tokens on every exchange — the returned Credentials carries the new one and must be persisted, or the next refresh 400s. should_refresh reports the 5-minute-ahead window so long-running processes renew before a wire call fails:
use smooai_client_shared::auth::refresh::{refresh_session, should_refresh};
if should_refresh(&creds) {
let fresh = refresh_session(&http, supabase_url, anon_key, &creds).await?;
store.save(&fresh)?; // MUST persist — the old refresh_token is now revoked
}RFC 6749 service-account grant: mint a client_id/client_secret in the Smoo web app, exchange for an org-scoped bearer at https://auth.smoo.ai/token (override with SMOOAI_AUTH_URL for staging):
use smooai_client_shared::auth::{m2m::client_credentials_grant, CredentialsStore};
// The token URL comes from token_url(): SMOOAI_AUTH_URL override, else auth.smoo.ai/token.
let creds = client_credentials_grant(&http, &client_id, &client_secret).await?;
CredentialsStore::default_m2m()?.save(&creds)?; // ~/.smooth/auth/smooai.jsonBoth flows share one on-disk shape. Two well-known files by convention — a single host carries a user session and an M2M session simultaneously without collision — written with mode 0600:
use smooai_client_shared::auth::{Credentials, CredentialsStore};
let store = CredentialsStore::default_user()?; // or ::default_m2m(), or ::at(path)
if let Some(creds) = store.load()? {
if creds.is_expired() { /* refresh or re-login */ }
}smooai-client-shared is not published to crates.io. Consume it as a git dependency — this is exactly how the th CLI consumes it in production (rev-pinned, features = ["auth"]):
[dependencies]
smooai-client-shared = { git = "https://github.com/SmooAI/client-shared.git", features = ["auth"] }| Feature | Adds | Pulls | Status |
|---|---|---|---|
auth |
Supabase OAuth + password + refresh, M2M, CredentialsStore |
tokio, reqwest, axum, serde, … |
✅ working, 28 unit tests |
auth is not a default. It is the crate's only surface and its one consumer always asks for it, but the tree it pulls in is heavy enough to be explicit about.
| Surface | Status |
|---|---|
Rust auth |
✅ Working — OAuth PKCE localhost-callback (387 LOC), password grant, refresh with rotation, M2M, 0600 CredentialsStore; 28 unit tests; consumed by the th CLI in production |
Rust llm |
❌ Not built — no module, no feature flag. Pearl th-f7b20f tracks it |
| crates.io | ❌ Not published — git dependency is the only install path |
| npm / NuGet / PyPI | 📦 Planned, no code — there is no src/, dotnet/ or python/ directory |
client-shared/
└── rust/ # smooai-client-shared (git dependency; crates.io planned)
├── Cargo.toml
└── src/
├── lib.rs
└── auth/ # oauth · password · refresh · m2m · storage (feature = "auth")
npm (src/), NuGet (dotnet/), and PyPI (python/) packages are roadmap, not directories.
SmooAI/ui— the design system: tokens, base CSS, the smoo monogram. Consumed directly by smooblue and observability-studio. This crate used to carry a copy; it no longer does.SmooAI/smooth— thethCLI; consumes this crate (features = ["auth"]) for login + credential storage.
@smooai/client-shared is built and open-sourced by Smoo AI — the AI-powered business platform with AI built into every product: CRM, customer support, campaigns, field service, observability, and developer tools.
- 🧰 More open source from Smoo AI — smoo.ai/open-source
- 🧩 Sibling repos — ui (the design-system-only crate), smooth (the
thCLI), @smooai/config, @smooai/logger
PRs welcome. cd rust && cargo test --all-features must pass; keep the bare ui build zero-dep and no_std, and gate anything heavier behind a feature flag.
MIT — see LICENSE.
Built by Smoo AI — AI built into every product.
