Test your passkeys at: https://axum-solid-playground.fly.dev
Features:
- Rust backend with axum
- Database integration: rusqlite
- SolidJS frontend with vite
- solid-ui for UI components
- Dev proxy for frontend in backend
- Prod: embed client js dist in rust binary
- Discoverable passkeys for authentication with webauthn-rs
- be: session management: tower-sessions-rusqlite-store
- be: roll expire of session on request (max every minute)
- be: session management: write informative cookie for fe
- fe: session management: AuthContext
- fe: session: detect expire and refresh ui
- Deployment (fly.io)
-
litefs for distributed SQLiteremoved, no websockets - PR maxcountryman/tower-sessions-stores#6
- publish crate tower-sessions-rusqlite-store
- rspc? cool idea but 🚫 no support for axum 0.7 and generally a big mess
- GraphQL with async-graphql for typed api between server and client
- allow users to register additional passkeys
- ui: passkey details
- security headers
- signout all my sessions
- ui: server info, debug network
- github action
- websockets?
- distributed kv?
- ssr?
- ... the possibilities are endless, the time so short
Playground to learn:
- how to combine a rust axum backend with a solidJS frontend?
- how to authenticate users with discoverable passkeys? and what's the user experience in different browsers?
- how to manage a session on the server with persistence and sync to the js client (on first render) (MVP)? - async sqlite in rust, yes please #no_orm?
- how cool is litefs and how to (ab)use it?
- how to have fun, go light and fast, with a small (<10MB) single standalone binary, that uses <100MB RAM idle.
- ... all PRs welcome 💓
- ... and all issues too 🤗
Copy .env.example
to .env
:
cd server
cp .env.example .env
cd ..
The rust backend includes a dev proxy for the frontend, so that the host and port of the fe and be is the same, no CORS issues arise, increased dev prod parity, good dev-ex with hot reloading.
In one terminal, run vite:
cd client
npm install
npm run dev
In another terminal, run the backend server:
cd server
cargo watch -x "run --features dev_proxy"
Open http://localhost:3000 to view it in your browser.
axum-embed is used to embed the frontend into the backend. For single binary niceness.
cd client
npm run build
cd ..
cargo build --release
./target/release/axum-solid-playground
The resulting binary is ~8MB.
Create volume initially:
fly launch --no-deploy
# if no volume created during initial launch:
fly volumes create playground --region ams --size 3
Set before the first deploy:
fly secrets set \
RP_ID=axum-solid-playground.fly.dev \
RP_ORIGIN=https://axum-solid-playground.fly.dev \
RP_NAME=axum-solid-playground \
DATABASE_URL=sqlite:///data/playground.db
fly deploy
image size: 104 MB (but as our binary is only ~8MB, this is what needs to be pushed in most cases)
Currently there is only one database on one volume (in ams). Litefs, which would enable distributed SQLite, was removed again, mainly to keep things simple and the limitations with websockets . Only one instance can be run at a time.
remove:
fly machine ls
fly m destroy <id>
fly volumes ls
fly volumes destroy <id>
SignUp and SignIn are implemented with passkeys with webauthn-rs.
tower-sessions with a custom RusqliteStore is used for session management.
The session is used for the passkey dance as well as to remember the authenticated user.
A cookie authenticated_user_js
(http_only=false) is set on successful signin so that the js frontend knows the user is authenticated and can render appropriatly on first load.
This cookie is only informative for the client and not used to determine if the user is authenticated on the server. No auth decision on the server is based on the cookie.
The session are rolled every minute (see: roll_expiry_mw). This also keeps the informative cookie fresh.
Chrome (local) passkeys can be managed at chrome://settings/passkeys.
Firefox and Safari on MacOS save them in the system keychain, which can be managed in Settings -> Passwords.