Minimalist TOTP-based forward-auth gateway for your Nginx reverse proxy.
Onetime is a lightweight, zero-password authentication layer designed to protect your private services. It relies purely on Time-based One-Time Passwords (TOTP) and integrates seamlessly with Nginx's auth_request module.
Onetime acts as a sidecar decision maker for your ingress controller.
sequenceDiagram
participant User
participant Nginx as Nginx Proxy
participant Onetime as Onetime Auth
participant App as Protected Service
User->>Nginx: GET /private
Nginx->>Onetime: GET /api/auth/nginx
alt Session Valid
Onetime-->>Nginx: 200 OK (X-Auth-User)
Nginx->>App: Forward Request
App-->>User: Response
else Session Invalid
Onetime-->>Nginx: 401 Unauthorized
Nginx-->>User: 302 Redirect to /login
end
- 🔑 Passwordless: Pure TOTP flow. No passwords to remember or leak.
- 🛡️ Secure Defaults: Built-in CSRF protection, signed HTTPOnly cookies, and rate limiting.
- 🎨 Modern UI: Polished Glassmorphism interface with fluid entry animations.
- ⚡ Lightweight: Built on Node.js/Express with SQLite (WAL mode) for minimal footprint.
- 🔌 Drop-in: Ready-to-use
nginx.confsnippets for Nginx Proxy Manager or standard Nginx.
This advanced configuration ensures security and persistence.
version: "3.8"
services:
onetime:
image: onetime
build: .
container_name: onetime
restart: unless-stopped
environment:
- NODE_ENV=production
- APP_NAME=Onetime
- PORT=3000
- TRUST_PROXY=true # Trust all proxies (or set to specific IP/CIDR)
- SESSION_TTL_SECONDS=86400
- COOKIE_DOMAIN=.example.com # Optional: Share cookie across subdomains
- SECURE_COOKIES=true # Require HTTPS
- SAME_SITE=lax # 'lax' is recommended, 'none' for cross-site
volumes:
- onetime_data:/app/data
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp
networks:
- default
volumes:
onetime_data: {}# 1. Build and start the container
docker-compose up -d --build
# 2. (Optional) Check logs
docker-compose logs -fNote: The container runs as a non-root user (node). Docker named volumes usually handle permissions automatically, but if mapping a host folder, ensure it is writable by UID 1000.
# 1. Install dependencies
npm install
# 2. Start the server
npm start
# Server listening on :3000Add this to your server block (or Nginx Proxy Manager "Advanced Configuration") to protect a location. This setup automatically redirects unauthenticated users to the login page.
Note: See
npm_onetime_advanced.conffor a template with placeholders.
location / {
# 1. Protect this location
auth_request /onetime-check;
# 2. Redirect 401 Unauthorized to Onetime Login
error_page 401 = @onetime_login;
proxy_pass http://your-private-service:8080;
}
# Internal Auth Check
location /onetime-check {
internal;
# Point to your Onetime container/host
proxy_pass http://localhost:3000/api/auth/nginx;
proxy_set_header Cookie $http_cookie;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
# Redirection Handler
location @onetime_login {
# Point to your public Onetime domain
return 302 https://auth.example.com/login?redirect_uri=$scheme://$http_host$request_uri;
}| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
Listening port |
APP_NAME |
Onetime |
Display name in UI |
COOKIE_NAME |
onetime_session |
Session cookie name |
SECURE_COOKIES |
false |
Set true behind HTTPS |
TRUST_PROXY |
loopback |
Proxy trust level (ip/subnets) |
Built with ❤️ for privacy enthusiasts.



