watchword 0.2.1

Authentication for axum servers and Yew frontends: password, Cloudflare Access and Google sign-in, sessions and user management.
Documentation

watchword

Authentication for axum servers and Yew frontends: password, Cloudflare Access and Google sign-in, sessions and user management.

The design, including the full API each layer grows into, is in docs/design.md.

Features

Each layer sits behind a cargo feature, so an app takes only what it uses. Server-only and browser-only dependencies never share a feature, so a wasm frontend can take yew023 without pulling in server code. No feature is on by default. On docs.rs, items that need a feature are marked with it.

A dependency that is not yet 1.0 and appears in the API is named after its version: its feature and module are axum08 for axum 0.8, sqll014 for sqll 0.14, musli01 for musli 0.1, musliweb04 for musli-web 0.4 and yew023 for yew 0.23. A later version is added beside it, so a watchword upgrade never forces an app onto a new axum, sqll, musli, musli-web or yew, and an app moves when it chooses.

Feature Module What it provides
(none) types UserId, Role, Access, Method, the wire types (UserInfo, AuthConfig, LoginRequest, CreatedUser, ...), AuthError and its Invalid reasons, validate_password, normalize_email, from the watchword-core crate
musli01 musli 0.1 Encode/Decode on the wire types
musliweb04 musliweb04 for musli-web 0.4: the account and user-management endpoints and the AuthBroadcast of live auth changes, as API types an app carries on its own musli-web WebSocket, with their ids pinned in 32000–32255; with axum08, Connection, which answers those endpoints on one socket and turns AuthEvents into the updates it may see; with yew023, AuthContext::set_socket, through which the components follow those broadcasts (implies musli01)
server core, store bcrypt password hashing with dummy_hash timing, session ids and link tokens, HMAC-signed session cookies; the Auth service (sign-in, registration links, user management, sign-in throttle, AuthEvents, bootstrap admin) and Auth::guard for WebSocket sessions; the Store trait and MemoryStore
cloudflare cloudflare Cloudflare Access JWT verification with a pluggable KeyFetcher (implies server)
google google Google OAuth: GoogleConfig for AuthBuilder::google; with axum08, routes() serves its start, registration, account-switch and callback routes (implies server)
axum08 axum08 for axum 0.8: the SignedIn, Admin, MaybeSignedIn and LiveSession extractors and routes() for the /api/auth JSON routes, plus the Cloudflare and Google routes when those features are on (implies server)
sqll014 sqll014 SqllStore, a Store over SQLite on a sqll 0.14 pool (its own, or nested in the app's), its SCHEMA, and docs/sqll/impersonator.sql, the one-time upgrade for a database created with an older schema (implies server)
test-util google::fake for an app's tests: AuthBuilder::google_endpoints, and with axum08 FakeGoogle, a local fake Google to sign in through offline (implies google)
yew023 yew023 for yew 0.23: AuthProvider, use_auth, SignIn, Register, AccountPanel, UserMenu, AdminUsers, CloudflareSettings, the http client and L10N, the components' messages as a phrasebook catalog, from the watchword-yew023 crate

The most-used names that do not depend on a versioned feature are re-exported at the crate root.

Using it

Add it from crates.io with the features each crate needs. The package and the library are both named watchword; a dependency key of auth keeps code referring to it as auth:

[dependencies]
# Server crate.
auth = { package = "watchword", version = "0.1", features = ["axum08", "cloudflare"] }
# Frontend crate.
auth = { package = "watchword", version = "0.1", features = ["yew023"] }

Example app

examples/app is an axum server and a Yew frontend wired up with all three sign-in methods, MemoryStore and a musli-web WebSocket held open for the session, which carries the components' account and admin requests and live updates beside the app's own API (musliweb04): a reference for apps taking the library, and a way to try the components by hand. It is its own cargo workspace, so it adds nothing to the library's builds or lockfile.

Build the frontend with trunk, then run the server, which serves it:

cd examples/app/frontend && trunk build
cd examples/app && cargo run -p watchword-example-server -- --bind 127.0.0.1:8080

The server prints a registration link for the bootstrap admin (admin by default, --bootstrap-admin NAME to change it); open it to choose a password and sign in. Everything is in memory, so a restart starts over. Google sign-in is on when GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are set, with the OAuth client's redirect URL at PUBLIC_URL/api/auth/google/redirect (--public-url, by default http:// plus the bind address). Cloudflare Access is configured on the app's Settings page, or from CLOUDFLARE_TEAM_DOMAIN and CLOUDFLARE_AUDIENCE; CLOUDFLARE_TRUST_EMAIL_HEADER=1 accepts Access's email header alone, for local testing only. --help lists the options.

trunk builds with the wasm-bindgen CLI matching examples/app/Cargo.lock, downloading it unless it is installed or already in trunk's cache.