axumPaseto Svelte Themes

Axumpaseto

Secure auth system — Rust (Axum) + PASETO V4 + SvelteKit + OAuth2/OIDC via Rauthy

AxumPaseto

Secure Auth System — Rust (Axum) + PASETO V4 + SvelteKit + OAuth2/OIDC via Rauthy

Secure authentication and authorization system built with Rust (Axum) and SvelteKit. Local authentication uses PASETO V4 tokens signed by HashiCorp Vault, with Redis Sentinel for session management and MariaDB for user storage. External authentication (OAuth2/OIDC via social providers) is supported through Rauthy as the identity provider.

  • PASETO V4.public tokens signed by HashiCorp Vault Transit (Ed25519)
  • Local login with bcrypt password verification
  • OAuth2/OIDC external authentication via Rauthy (Google, GitHub, and more)
  • Role-based access control enforced entirely in the Rust backend
  • Redis Sentinel for session and role caching; MariaDB for user storage
  • SvelteKit SPA with Svelte 5, Tailwind v4, and OpenAPI-generated TypeScript types

Screenshots

Login

Dashboard

Users List

Auth Providers

Table of Contents

Architecture

graph LR
    spa["SvelteKit SPA\n(frontend)"]
    nginx["Nginx :8080\n(reverse proxy)"]
    issuer["Issuer :3000\n(auth service)"]
    api["API :3001\n(protected API)"]
    rauthy["Rauthy :8090\n(IdP / OAuth2)"]

    subgraph infra["Infrastructure"]
        mariadb["MariaDB"]
        redis["Redis Sentinel"]
        vault["Vault\n(Transit / Ed25519)"]
    end

    spa      -->|HTTP| nginx
    nginx    -->|auth routes| issuer
    nginx    -->|protected routes| api

    issuer   -->|user storage| mariadb
    issuer   -->|session / role cache| redis
    issuer   -->|sign tokens| vault
    issuer   -->|BFF token exchange| rauthy

    api      -->|role lookup| redis
    api      -->|verify tokens| vault

    spa      -->|"OAuth2/OIDC\nlogin redirect"| rauthy

Backend Services (Cargo Workspace)

Crate Role Depends On
rust-backend-issuer Login, token refresh, logout, OAuth2/OIDC MariaDB, Redis, Vault
rust-backend-api Protected endpoints (dashboard, admin) Redis, Vault
shared Domain traits, adapters, middleware

Clean Architecture Layers

Handlers (HTTP) → Services (business logic) → Domain (traits/models) ← Adapters (implementations)
  • Domainshared/src/domain/ — pure traits and models, no external dependencies
  • Servicesissuer/src/services/, api/src/services/ — use-case implementations
  • Handlersissuer/src/handlers/, api/src/handlers/ — Axum HTTP handlers
  • Adaptersshared/src/adapters/ (Redis, Vault), issuer/src/adapters/mariadb/ (sqlx)
  • Middlewareshared/src/middleware/role_middleware.rs — PASETO verification + role-based access control

Authentication Flow

  1. LoginPOST /v1/auth/login verifies credentials (bcrypt), issues a PASETO V4.public access token signed by Vault Transit (Ed25519), and stores a random refresh token in Redis.
  2. Access token — short-lived (configurable, default 600 s), returned in the response body and held in the Svelte in-memory auth store.
  3. Refresh token — random string stored in an HttpOnly / SameSite=Strict cookie (default 7 days), dual-key mapped in Redis (refresh_token:by_token:{token}refresh_token:by_uid:{uid}).
  4. Authorizationrole_middleware verifies the PASETO token, fetches roles from Redis (role:{uid}), and enforces RequiredRoles. Returns 401 Unauthorized or 403 Forbidden on failure.
  5. OAuth2 / OIDC — The issuer service acts as an OAuth2 authorization server and BFF (Backend For Frontend). A built-in bff confidential client handles server-side token exchange. Additional clients can be registered via OAUTH_CLIENTS_JSON.

Tech Stack

Layer Technology
Backend Framework Axum 0.8
Tokens PASETO V4.public (pasetors)
Key Management HashiCorp Vault Transit (Ed25519)
Database MariaDB (sqlx)
Cache / Sessions Redis + Sentinel (deadpool-redis)
OAuth2 / OIDC Provider Rauthy 0.34.3
Frontend SvelteKit 2 + Svelte 5 (static SPA)
Styling Tailwind CSS v4
API Client openapi-fetch
API Docs utoipa + Swagger UI
Reverse Proxy Nginx 1.29
E2E Tests Playwright

Why PASETO over JWT?

PASETO (Platform-Agnostic Security Tokens) was chosen over JWT for the following reasons:

Concern JWT PASETO V4.public
Algorithm agility Vulnerable — alg: none and weak algorithm substitution attacks are well-known Not applicable — algorithm is fixed by the version (v4.public = Ed25519)
Key confusion RS256 vs HS256 confusion attacks possible No confusion — public/secret key roles are unambiguous
Signing Any algorithm selectable at runtime Ed25519 only, signed by Vault Transit (HSM-backed)
Footer Not standardized Structured, authenticated footer for key ID and metadata
Spec clarity Complex, many optional parts Simple, opinionated, hard to misuse

In this project, PASETO tokens are signed by HashiCorp Vault Transit (Ed25519), meaning the private key never leaves Vault — adding an additional layer of protection even if the application server is compromised.

Getting Started

Prerequisites

  • Rust (edition 2024, stable toolchain)
  • Node.js 18 or later
  • Docker or Podman

Docker (full stack)

Copy the example environment file and fill in your values before starting the stack:

cp docker/.env.example docker/.env
# Edit docker/.env with your values (Vault token, database credentials, BFF secret, etc.)

Then start all services:

cd docker
docker compose up -d

This starts: Nginx, Issuer, API, Vault, MariaDB, Redis (master + sentinel), and Rauthy (OAuth2/OIDC provider).

Note — Development environment only: All inter-service communication uses HTTP. When deploying to production, switch all endpoints to HTTPS.

Note — Rauthy setup required: After starting the Docker stack, Rauthy must be configured before OAuth2/OIDC login works. See the Rauthy OAuth2/OIDC Provider Setup section in docker/README.md for instructions.

Test accounts: Pre-seeded development accounts (email / password / roles) are listed in docker/README.md.

To build and serve the frontend, use the build profile:

# Build the SvelteKit SPA and copy static assets into the shared volume
docker compose --profile build run --rm frontend

Backend (development)

# Build all workspace crates
cargo build --manifest-path backend/Cargo.toml

# Build a specific crate
cargo build --manifest-path backend/Cargo.toml -p rust-backend-issuer
cargo build --manifest-path backend/Cargo.toml -p rust-backend-api

# Run tests
cargo test --manifest-path backend/Cargo.toml

# Lint and format
cargo clippy --manifest-path backend/Cargo.toml
cargo fmt --manifest-path backend/Cargo.toml

Frontend (development)

cd frontend
npm install
npm run dev       # Vite dev server at :5173
npm run build     # Static SPA build
npm run check     # Svelte type checking
npm run lint      # ESLint + Prettier
npm test          # Vitest

E2E Testing

End-to-end tests are located in e2e/ and run against the full Docker stack at http://localhost:8080.

cd e2e
cp .env.example .env   # fill in test user credentials
npm install
npx playwright install
npx playwright test --project=chromium

For full setup instructions, CI configuration, and the complete test case list, see e2e/README.md.

Configuration

Environment variables are loaded via dotenvy + envy (type-safe deserialization into config structs). Copy docker/.env.example to docker/.env and edit the values before running Docker Compose. Never commit secrets to version control.

cp docker/.env.example docker/.env
# Edit docker/.env with your values

Issuer Service Variables

Variable Description
RUST_LOG Log level (e.g. info, debug)
SERVER_PORT HTTP listen port
COOKIE_SECURE Set true in production (requires HTTPS)
CORS_ORIGINS Comma-separated list of allowed CORS origins
ACCESS_TOKEN_ISSUER PASETO token issuer claim (your domain)
ACCESS_TOKEN_TTL Access token lifetime in seconds
VAULT_URL Vault server address
VAULT_TOKEN Vault authentication token — use a secret manager in production
VAULT_ALLOW_HTTP Set true only in development; must be false in production
MOUNT_PATH Vault Transit mount path
TRANSIT_KEY Vault Transit key name
MARIADB_URL MariaDB connection string — keep secret
REDIS_SENTINELS Comma-separated sentinel addresses
REDIS_MASTER_NAME Redis Sentinel master name
REDIS_REFRESH_TOKEN_TTL Refresh token lifetime in seconds
REDIS_ROLE_TTL Role cache lifetime in seconds
REDIS_NAMESPACE_* Redis key namespace prefixes (see .env.example)
BFF_CLIENT_SECRET Secret for the built-in BFF OAuth2 client — use a long random string in production
BFF_REDIRECT_URI Callback URI registered for the BFF client
MOBILE_REDIRECT_URIS Comma-separated redirect URIs for the built-in mobile public client (optional)
OAUTH_CLIENTS_JSON JSON array of additional OAuth2 client definitions (optional)

API Service Variables

Variable Description
RUST_LOG Log level
SERVER_PORT HTTP listen port
ACCESS_TOKEN_ISSUER Must match the issuer service value
VAULT_URL Vault server address
VAULT_TOKEN Vault authentication token
VAULT_ALLOW_HTTP Development-only HTTP override
MOUNT_PATH Vault Transit mount path
TRANSIT_KEY Vault Transit key name
REDIS_SENTINELS Comma-separated sentinel addresses
REDIS_MASTER_NAME Redis Sentinel master name
REDIS_NAMESPACE_UID_TO_ROLE Redis key namespace prefix for role lookups

See docker/.env.example for the full list of variables and their descriptions.

API Endpoints

Issuer Service (Authentication)

Method Path Auth Description
GET /auth/v1/login Initiate local or OAuth2 login
POST /auth/v1/login/local Authenticate with username and password (rate-limited)
GET /auth/v1/callback OAuth2 authorization code callback (BFF flow)
POST /auth/v1/refresh Refresh the access token via HttpOnly cookie
POST /auth/v1/logout Invalidate session and clear cookie
GET /auth/v1/oauth/init Initiate external OAuth2/OIDC login (Rauthy)
GET /auth/v1/oauth/callback External OAuth2 callback
POST /auth/v1/oauth/link Link external OAuth2 account
GET /users/v1 Admin List users
POST /users/v1 Admin Create user
PUT /users/v1/{user_id} Admin Update user
DELETE /users/v1/{user_id} Admin Delete user
GET /auth/v1/providers Admin List auth providers
POST /auth/v1/providers Admin Create auth provider
PUT /auth/v1/providers/{id} Admin Update auth provider
DELETE /auth/v1/providers/{id} Admin Deactivate auth provider
GET /.well-known/openid-configuration OIDC Discovery endpoint
GET /.well-known/jwks.json OIDC JWKS endpoint
GET /userinfo User, Admin OIDC userinfo endpoint

API Service (Protected)

Method Path Auth Description
GET /api/dashboard User, Admin Dashboard data
GET /swagger-ui Interactive API documentation (utoipa)

Project Structure

.
├── backend/
│   ├── Cargo.toml              # Workspace root (resolver=2, edition 2024)
│   ├── issuer/                 # Authentication service (rust-backend-issuer)
│   │   └── src/
│   │       ├── handlers/       # login, logout, refresh, OAuth2/OIDC, users
│   │       ├── services/       # AuthenticationService, IdTokenVerifier
│   │       ├── adapters/mariadb/
│   │       └── config.rs
│   ├── api/                    # Protected API service (rust-backend-api)
│   │   └── src/
│   │       ├── handlers/       # dashboard, admin
│   │       ├── services/       # SystemMonitor
│   │       └── config.rs
│   └── shared/                 # Common library crate
│       └── src/
│           ├── domain/         # Traits and models (no external deps)
│           ├── adapters/       # Redis, Vault, Authorization implementations
│           └── middleware/     # role_middleware (PASETO + RBAC)
├── frontend/                   # SvelteKit SPA (Svelte 5, Tailwind v4)
│   └── src/
│       ├── lib/
│       │   ├── api/            # openapi-fetch typed client
│       │   ├── components/ui/  # Shared UI components
│       │   └── stores/         # Svelte 5 $state auth store
│       └── routes/
│           ├── login/
│           └── (protected)/    # dashboard, users, auth-providers
├── e2e/                        # Playwright end-to-end tests
│   ├── playwright.config.ts
│   ├── helpers/
│   │   └── login.ts            # loginAs() / navigateSPA() helpers
│   └── tests/
│       └── auth.spec.ts        # GROUP A — Authentication tests
├── docker/
│   ├── docker-compose.yml
│   ├── .env.example            # Template — copy to .env before running
│   ├── frontend/               # Frontend builder Dockerfile
│   ├── mariadb/                # Database initialization scripts
│   ├── nginx/                  # Nginx reverse proxy configuration
│   ├── rauthy/                 # Rauthy OAuth2/OIDC provider config
│   ├── redis/                  # Redis Sentinel configuration
│   ├── rust_app/               # Dockerfile for Rust services
│   └── vault/                  # Vault dev setup scripts
└── documents/                  # Design documents and test scope

Networks (Docker)

Network Services
backend-net nginx, rust_issuer, rust_api
redis-net redis-master, sentinel-1, rust_issuer, rust_api
mariadb-net mariadb, rust_issuer
vault-net vault, rust_issuer, rust_api
rauthy-net rauthy, rust_issuer

Exposed Ports

Port Service Bound To Notes
8080 nginx 127.0.0.1 Frontend + API gateway (primary entry point)
8090 rauthy 127.0.0.1 OAuth2 / OIDC provider UI
3000 rust_issuer 0.0.0.0 Direct backend access (development only)
3001 rust_api 0.0.0.0 (mapped to internal 3000) Direct backend access (development only)
3306 mariadb 127.0.0.1 Database (development only)
6379 redis-master 127.0.0.1 Redis (development only)
26379 sentinel-1 127.0.0.1 Redis Sentinel (development only)

In production, ensure that ports 3000, 3001, 3306, 6379, and 26379 are not exposed to the public internet.

Contributing

Pull requests and issues are welcome. Please follow the project conventions:

  • Use Conventional Commits for all commit messages.
  • Ensure cargo clippy and cargo fmt pass before submitting a PR.
  • Include or update tests for any changed behavior. Verify 403 / 401 flows on new features.
  • All code comments and documentation must be written in English.

License

This project is released under the MIT License. See the LICENSE file for details.

Top categories

Loading Svelte Themes