Skip to content

Latest commit

 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@smooai/client-shared — Shared primitives for every Smoo AI client

Smoo AI license consumed by the th CLI

OAuth PKCE localhost callback M2M client credentials 0600 credential store PKCE M2M 0600 store

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_credentials grant — and one 0600 on-disk credential store holding user and machine sessions side by side. Consumed in production by the th CLI. Rust-only today; not yet on crates.io — a git dependency is the install path.

What is this?

The auth plumbing every Smoo AI Rust client needs identically, in one crate instead of re-implemented per app:

  1. Sign a human in — browser OAuth (PKCE + localhost callback), or email+password where no browser exists.
  2. Keep them signed in — refresh-token grant with Supabase's rotation handled, plus a renew-ahead window.
  3. Sign a machine in — the RFC 6749 client_credentials grant against auth.smoo.ai/token.
  4. Put the credentials somewhere sane — a 0600 store 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 imported smooai_client_shared::ui, while the real design-system consumers (smooblue, observability-studio) depended on SmooAI/ui directly. 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/ui owns the design system; this crate is auth.

Feature tour

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/.

🔐 Browser OAuth (PKCE + localhost callback)

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 0600

Prerequisite: http://localhost must be in the Supabase project's Redirect URLs allowlist, and PKCE enabled (GoTrue ≥ v2.95 default).

🔑 Email + password grant

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?;

♻️ Session refresh

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
}

🤖 M2M client_credentials

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.json

💾 The credentials store

Both 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 */ }
}

Quickstart

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 flags

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.

Honest status

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

Layout

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.

Related repos

  • SmooAI/uithe 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 — the th CLI; consumes this crate (features = ["auth"]) for login + credential storage.

🧩 Part of Smoo AI

@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.

🤝 Contributing

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.

📄 License

MIT — see LICENSE.


Built by Smoo AI — AI built into every product.

About

Cross-runtime shared client library for Smoo AI (auth: OAuth, M2M, password; storage). Consumed by the smooth (th) CLI.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages