πŸ” Passwordless Authentication

Self-hosted, modern authentication server built with Rust. No passwords, no compromises.

πŸ¦€ Rust ⚑ Axum Framework πŸ”‘ WebAuthn πŸ“§ Magic Links πŸ” TOTP 🎫 JWT Sessions πŸ—„οΈ SQLite 🐳 Docker Ready πŸ“ MIT License

Overview

A modern, secure, and self-hosted passwordless authentication solution

πŸ’‘ What is Passwordless Auth?

Passwordless Auth is a self-hosted identity and authentication server designed for small teams and internal tools. It provides modern passwordless login mechanismsβ€”magic links delivered via email, WebAuthn (passkeys), and TOTP fallbackβ€”while issuing JWTs for session management. No third-party identity provider is required; all state is under your control, with revocable short-lived sessions and refresh tokens.

🎯 Goals & Motivation

🚫

Eliminate Passwords

Reduce risk by avoiding traditional passwords in favor of magic links, passkeys, and one-time TOTP codes.

🏠

Self-Hosted Control

No reliance on external SaaS for auth; all data, tokens, and credentials live under your infrastructure.

✨

Modern UX

Support for WebAuthn (biometric or hardware keys), magic link convenience, and TOTP as backup.

πŸ”’

Secure Sessions

JWT access + refresh tokens with revocation and lifetime policies for maximum security.

πŸ“¬

Reliable Delivery

Email queueing with retries to ensure magic links arrive even under transient SMTP issues.

⚑

High Performance

Built with Rust for safety, speed, and efficiency. Production-ready from day one.

Core Features

Three powerful authentication methods, one unified system

1

πŸ“§ Magic Link Flow

Email-based one-time login links with expiration and single-use semantics. Perfect for quick, secure access without memorizing credentials.

  • βœ“ Email-based authentication
  • βœ“ One-time use tokens
  • βœ“ Configurable expiration
  • βœ“ Reliable queue system
2

πŸ” TOTP Flow

Time-based one-time passwords for fallback or second factor. Compatible with Google Authenticator, Authy, and other TOTP apps.

  • βœ“ Industry-standard TOTP
  • βœ“ QR code enrollment
  • βœ“ Clock skew tolerance
  • βœ“ Offline capable
3

πŸ”‘ WebAuthn Flow

Strong cryptographic passwordless authentication using platform or roaming authenticators. Biometric and hardware key support.

  • βœ“ FIDO2 compliant
  • βœ“ Biometric authentication
  • βœ“ Hardware key support
  • βœ“ Phishing resistant

πŸ”§ Core Technologies

Technology Purpose Benefits
πŸ¦€ Rust Implementation language Memory safety, performance, reliability
⚑ Axum HTTP server framework Fast, ergonomic, type-safe web APIs
πŸ—„οΈ SQLite Embedded persistence Light, portable, zero-config database
🎫 JWT Token-based sessions Stateless, scalable authentication
πŸ“ OpenAPI API specification Auto-generated clients, documentation
🐳 Docker Containerization Consistent deployment, easy orchestration

Architecture

Comprehensive system design with visual flow diagrams

πŸ—οΈ High-Level Architecture Overview

flowchart TD
    User[User / Client]
    Choose[Select Auth Method]
    User --> Choose

    subgraph MagicLink[Magic Link Flow]
        direction TB
        MLRequest[POST /request/magic]
        CreateToken[Generate one-time token]
        EnqueueEmail[Enqueue email job]
        EmailWorker[Email worker picks job]
        SendEmail[Send via SMTP]
        ClickLink[User clicks link]
        VerifyToken[Validate and mark used]
        IssueJWT_ML[Issue JWTs]
    end

    subgraph TOTP[TOTP Flow]
        direction TB
        TOTPEnroll[POST /totp/enroll]
        StoreTOTP[Store TOTP secret]
        TOTPVerify[POST /totp/verify]
        ValidateTOTP[Validate code]
        IssueJWT_TOTP[Issue JWTs]
    end

    subgraph WebAuthn[WebAuthn Flow]
        direction TB
        WebRegOptions[POST /webauthn/register/options]
        CompleteReg[POST /webauthn/register/complete]
        SaveCred[Persist credential]
        WebLoginOptions[POST /webauthn/login/options]
        CompleteLogin[POST /webauthn/login/complete]
        IssueJWT_Web[Issue JWTs]
    end

    subgraph Tokens[Token Management]
        direction TB
        RefreshReq[POST /token/refresh]
        ValidateRefresh[Validate refresh token]
        IssueNewJWT[Issue new JWTs]
    end

    subgraph DB[SQLite Database]
        direction TB
        Users[Users Table]
        MagicTokens[Magic Link Tokens]
        TOTPSecrets[TOTP Secrets]
        WebCreds[WebAuthn Credentials]
        RefreshTokens[Refresh Tokens]
        EmailQueue[Email Queue]
    end

    Choose --> MLRequest
    MLRequest --> CreateToken --> EnqueueEmail
    EnqueueEmail --> EmailQueue
    EmailQueue --> EmailWorker --> SendEmail
    SendEmail --> ClickLink --> VerifyToken --> IssueJWT_ML --> User

    Choose --> TOTPEnroll --> StoreTOTP
    StoreTOTP --> TOTPSecrets
    TOTPVerify --> ValidateTOTP --> IssueJWT_TOTP --> User

    Choose --> WebRegOptions --> CompleteReg --> SaveCred
    SaveCred --> WebCreds
    WebLoginOptions --> CompleteLogin --> IssueJWT_Web --> User

    User --> RefreshReq --> ValidateRefresh
    ValidateRefresh --> RefreshTokens
    ValidateRefresh --> IssueNewJWT --> User
                

πŸ“Š Component Class Diagram

classDiagram
    class AuthServer {
        +Config config
        +start()
        +shutdown()
    }

    class MagicLinkService {
        +request_link(email)
        +verify(token)
        +generate_token()
    }

    class TOTPService {
        +enroll(email)
        +verify(email, code)
        +generate_secret()
    }

    class WebAuthnService {
        +begin_registration(email)
        +complete_registration(pending_id, resp)
        +begin_login(email)
        +complete_login(pending_id, resp)
    }

    class JWTService {
        +create_access(user_id)
        +create_refresh(user_id)
        +verify(token)
        +revoke_refresh(token)
    }

    class EmailQueueWorker {
        +enqueue(to, subject, text, html)
        +process_due()
        +retry_failed()
    }

    class SMTPProvider {
        +send(to, subject, body)
        +configure(host, port, username, password)
    }

    class SQLiteDB {
        +execute(query)
        +query(query)
        +transaction()
    }

    class User {
        -String id
        -String email
        -String totp_secret
        -credentials
    }

    class MagicLinkToken {
        -String token
        -DateTime expires_at
        -bool used
        -String user_id
    }

    class RefreshToken {
        -String token
        -DateTime expires_at
        -bool revoked
        -String user_id
    }

    AuthServer --> MagicLinkService
    AuthServer --> TOTPService
    AuthServer --> WebAuthnService
    AuthServer --> JWTService
    AuthServer --> SQLiteDB

    MagicLinkService --> SQLiteDB
    MagicLinkService --> EmailQueueWorker
    EmailQueueWorker --> SMTPProvider
    EmailQueueWorker --> SQLiteDB

    TOTPService --> SQLiteDB
    WebAuthnService --> SQLiteDB
    JWTService --> SQLiteDB

    SQLiteDB --> User
    SQLiteDB --> MagicLinkToken
    SQLiteDB --> RefreshToken
                

πŸ”„ Authentication Sequence

sequenceDiagram
    participant U as User
    participant C as Client App
    participant S as Auth Server
    participant DB as Database
    participant SMTP as SMTP Server

    Note over U,SMTP: Magic Link Authentication Flow

    U->>C: Enter email
    C->>S: POST /request/magic
    S->>DB: Create magic token
    S->>DB: Enqueue email
    S-->>C: 200 OK silent

    loop Email Worker
        S->>DB: Fetch pending emails
        S->>SMTP: Send magic link
        SMTP-->>U: Email delivered
    end

    U->>C: Click magic link
    C->>S: GET /verify/magic
    S->>DB: Validate token
    S->>DB: Mark token as used
    S->>DB: Store refresh token
    S-->>C: Return tokens
    C->>C: Store tokens

    Note over U,SMTP: Subsequent Requests

    C->>S: API request with access token
    S->>S: Verify JWT
    S-->>C: Protected resource

    Note over U,SMTP: Token Refresh

    C->>S: POST /token/refresh
    S->>DB: Validate refresh token
    S->>DB: Create new refresh token
    S-->>C: Return new tokens
                

Quick Start

Get up and running in minutes

βœ… Prerequisites
  • Rust toolchain (install via rustup)
  • Cargo (included with Rust)
  • Make (optional, for convenience commands)
  • Docker & Docker Compose (optional, for containerized deployment)

πŸ“₯ Installation

bash
# Clone the repository
git clone https://github.com/yourusername/passwordless-auth.git
cd passwordless-auth

# Build the project
make build

# Or build manually
cargo build --release

βš™οΈ Configuration

Edit config.toml with your settings:

config.toml
# JWT Configuration
jwt_secret = "your-super-secret-key-here-min-32-chars"
access_token_expiry_seconds = 900      # 15 minutes
refresh_token_expiry_seconds = 604800  # 7 days

# Magic Link Settings
magic_link_expiry_seconds = 600
magic_link_base_url = "http://localhost:3000/verify/magic"

# SMTP Configuration
smtp_host = "smtp.example.com"
smtp_port = 587
smtp_username = "your-email@example.com"
smtp_password = "your-smtp-password"
email_from = "no-reply@example.com"

# WebAuthn Settings
webauthn_rp_id = "localhost"
webauthn_origin = "http://localhost:3000"
webauthn_rp_name = "Passwordless Auth"

# Database
database_path = "auth.db"

πŸš€ Running the Server

bash
# Start the auth server
./target/release/passwordless-auth

# In another terminal, start the email worker
./target/release/email-worker

# Or use Docker Compose
docker-compose up --build

πŸ§ͺ Testing the API

bash
# Request a magic link
curl -X POST http://localhost:3000/request/magic \
  -H "Content-Type: application/json" \
  -d '{"email":"alice@example.com"}'

# Verify the magic link (get token from email or database)
curl "http://localhost:3000/verify/magic?token=YOUR_TOKEN_HERE"

# Response: {"access_token":"...", "refresh_token":"..."}

# Refresh tokens
curl -X POST http://localhost:3000/token/refresh \
  -H "Content-Type: application/json" \
  -d '{"refresh_token":"YOUR_REFRESH_TOKEN"}'

API Reference

Complete HTTP API documentation

πŸ“§ Magic Link Endpoints

POST /request/magic

Description: Request a magic login link to be sent via email

Request Body:

{
  "email": "alice@example.com"
}

Response: 200 OK (always succeeds silently to prevent email enumeration)

GET /verify/magic

Description: Verify a magic link token and receive JWTs

Query Parameters:

  • token (required): The magic link token from email

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

πŸ” TOTP Endpoints

POST /totp/enroll

Description: Enroll a TOTP authenticator for an email

Request Body:

{
  "email": "alice@example.com"
}

Response:

{
  "secret": "JBSWY3DPEHPK3PXP",
  "otpauth_url": "otpauth://totp/PasswordlessAuth:alice@example.com?secret=JBSWY3DPEHPK3PXP&issuer=PasswordlessAuth"
}
POST /totp/verify

Description: Verify a TOTP code and receive JWTs

Request Body:

{
  "email": "alice@example.com",
  "code": "123456"
}

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

πŸ”‘ WebAuthn Endpoints

POST /webauthn/register/options

Description: Get WebAuthn registration options

Request Body:

{
  "email": "alice@example.com"
}

Response: WebAuthn PublicKeyCredentialCreationOptions

POST /webauthn/register/complete

Description: Complete WebAuthn registration

Request Body:

{
  "pending_id": "uuid-from-options-response",
  "response": { /* PublicKeyCredential from browser */ }
}
POST /webauthn/login/options

Description: Get WebAuthn login options

Request Body:

{
  "email": "alice@example.com"
}

Response: WebAuthn PublicKeyCredentialRequestOptions

POST /webauthn/login/complete

Description: Complete WebAuthn login and receive JWTs

Request Body:

{
  "pending_id": "uuid-from-options-response",
  "response": { /* PublicKeyCredential from browser */ }
}

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

🎫 Token Management

POST /token/refresh

Description: Refresh access token using refresh token

Request Body:

{
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Documentation

Comprehensive guides and references

πŸ“š Key Concepts

πŸ” Security Primitives

  • HMAC & Signed Tokens: JWTs are signed using HS256 with a secret key to prevent forgery
  • Challenge/Response: WebAuthn uses cryptographic challenges to prove credential possession
  • One-time Protection: Magic link tokens are marked as used and expire after single use
  • TOTP Windowing: Small clock skew tolerance while preventing code reuse
  • Token Revocation: Refresh tokens stored server-side for immediate revocation capability

πŸ“§ Email Queue Worker

To improve reliability of magic link delivery, emails are enqueued in email_queue and retried with exponential backoff:

  • Fetches due pending emails from queue
  • Marks emails as "sending" to prevent duplicate delivery
  • Attempts delivery via SMTP
  • On failure: updates next_try_at with exponential backoff
  • On success: marks email as sent

This design makes the system resilient to transient SMTP issues.

πŸ§ͺ Testing

Unit Tests: Cover JWT creation/verification, magic link lifecycle, TOTP generation/validation, and refresh token management.

Integration Tests: Simulate full authentication flows including magic link end-to-end, token refresh, TOTP enrollment/verification, and WebAuthn option retrieval.

bash
# Run all tests
make test

# Or directly with cargo
cargo test

# Run specific test
cargo test test_magic_link_flow

🐳 Docker Deployment

Deploy using Docker Compose for production-ready setup:

bash
# Build Docker image
make docker-build

# Start all services
docker-compose up -d

# View logs
docker-compose logs -f

# Stop services
docker-compose down

Services:

  • auth: Primary authentication server (port 3000)
  • email-worker: Background email queue processor

πŸ”’ Security Considerations

⚠️ Production Security Checklist
  • Secret Management: Use strong, random jwt_secret (min 32 chars). Store in environment variables or secrets manager.
  • Token Expiry: Keep access tokens short-lived (15 min). Refresh tokens should be revocable.
  • TLS/HTTPS: Always deploy behind TLS termination (Caddy, Nginx, or cloud load balancer).
  • Rate Limiting: Implement per-IP/email throttling to prevent abuse and enumeration.
  • Email Security: Use SPF, DKIM, and DMARC for email authentication.
  • Database Backups: Regular backups of SQLite database containing users and tokens.
  • Monitoring: Log authentication attempts, failures, and token issuance for audit trails.
  • WebAuthn Origin: Ensure webauthn_origin matches your actual domain.

πŸ› οΈ Troubleshooting

Issue Possible Cause Solution
Magic link email not arriving SMTP misconfiguration Check email_queue table, verify SMTP credentials, run email-worker
Token expired error Token lifetime passed Request new magic link or use refresh token
JWT verification fails Wrong secret or malformed token Verify jwt_secret matches across all instances
WebAuthn errors Origin/RP mismatch Ensure webauthn_origin and rp_id match client domain
Database locked Concurrent SQLite access WAL mode enabled by default; avoid long transactions
Refresh token invalid Token revoked or expired Re-authenticate via magic link, TOTP, or WebAuthn

πŸš€ Extension Points

πŸ’Ύ

Backend Swap

Replace SQLite with PostgreSQL or MySQL for larger deployments and better concurrency.

πŸ“Š

Session Management

Add endpoints to list active sessions and revoke specific refresh tokens per user.

⏱️

Rate Limiting

Implement per-IP and per-email throttling using middleware or external service.

πŸ“§

Rich Email Templates

Upgrade to Handlebars or Tera templating for branded HTML emails.

πŸ“ˆ

Metrics & Monitoring

Export Prometheus metrics for auth success/failure, queue depth, and token issuance.

🎨

Admin Dashboard

Build a web UI to manage users, view challenges, and revoke tokens.

Contributing

Help make Passwordless Auth even better

🀝 How to Contribute
  1. Fork the repository on GitHub
  2. Create a feature branch: git checkout -b feature/your-feature
  3. Make your changes and add tests
  4. Ensure all tests pass: cargo test
  5. Format code: cargo fmt
  6. Run clippy: cargo clippy
  7. Commit your changes with clear messages
  8. Push to your fork and submit a pull request

πŸ’Ž High-Value Contributions

Glossary

Term Definition
Magic Link One-time login URL sent by email that expires after use
TOTP Time-based One-Time Password, standard for 2FA authenticator apps
WebAuthn Browser-native cryptographic authentication standard (FIDO2)
JWT JSON Web Token, signed token representing session identity
Access Token Short-lived JWT for API access (typically 15 minutes)
Refresh Token Longer-lived token for obtaining new access tokens (typically 7 days)
Email Queue Persistent queue with retry mechanism for reliable email delivery
RP (Relying Party) The service (this server) in WebAuthn terminology
Passkey User-friendly term for WebAuthn credentials (e.g., fingerprint, Face ID)