{"id":50782689,"url":"https://github.com/arumes31/rauth","last_synced_at":"2026-06-12T05:01:25.199Z","repository":{"id":333582245,"uuid":"991585322","full_name":"arumes31/rauth","owner":"arumes31","description":"Lightweight, high-performance authentication proxy and user identity management gateway featuring WebAuthn (Passkeys), TOTP, AES-256 session encryption, and Prometheus metrics. Fully compatible with Caddy, Nginx, and Traefik.","archived":false,"fork":false,"pushed_at":"2026-06-09T19:04:46.000Z","size":3948,"stargazers_count":30,"open_issues_count":42,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-09T20:09:21.086Z","etag":null,"topics":["auth-proxy","caddy","go","identity-provider","nginx-auth-request","passkey","redis","security-gateway","self-hosted","traefik-middleware","webauthn"],"latest_commit_sha":null,"homepage":"https://github.com/arumes31/rauth","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arumes31.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-27T21:11:55.000Z","updated_at":"2026-06-06T13:31:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/arumes31/rauth","commit_stats":null,"previous_names":["arumes31/rauth"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/arumes31/rauth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arumes31%2Frauth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arumes31%2Frauth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arumes31%2Frauth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arumes31%2Frauth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arumes31","download_url":"https://codeload.github.com/arumes31/rauth/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arumes31%2Frauth/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34229624,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["auth-proxy","caddy","go","identity-provider","nginx-auth-request","passkey","redis","security-gateway","self-hosted","traefik-middleware","webauthn"],"created_at":"2026-06-12T05:01:22.241Z","updated_at":"2026-06-12T05:01:25.165Z","avatar_url":"https://github.com/arumes31.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"static/favicon.png\" alt=\"RAuth Logo\" width=\"128\" height=\"128\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\" style=\"border-bottom: none;\"\u003e\u003cspan style=\"color: #ef4444;\"\u003eRAuth\u003c/span\u003e: High-Performance Auth Proxy \u0026 Identity Management\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Security-Hardened-red?style=for-the-badge\u0026logo=shield\" alt=\"Security Hardened\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/MFA-Passkey%20%2F%20TOTP-red?style=for-the-badge\u0026logo=yubico\" alt=\"MFA Support\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Performance-High--Speed-red?style=for-the-badge\u0026logo=go\" alt=\"High Performance\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/github/go-mod/go-version/arumes31/rauth?label=Go\u0026logo=go\u0026color=EA4335\" alt=\"Go Version\"\u003e\n  \u003cimg src=\"https://img.shields.io/github/license/arumes31/rauth?label=License\u0026color=EA4335\" alt=\"License\"\u003e\n  \u003cimg src=\"https://img.shields.io/github/last-commit/arumes31/rauth/main?color=EA4335\" alt=\"Last Commit\"\u003e\n  \u003cimg src=\"https://img.shields.io/github/repo-size/arumes31/rauth?color=EA4335\" alt=\"Repo Size\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/arumes31/rauth/actions/workflows/tests.yml\"\u003e\n    \u003cimg src=\"https://github.com/arumes31/rauth/actions/workflows/tests.yml/badge.svg?branch=main\" alt=\"Tests Status\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/arumes31/rauth/actions/workflows/build.yml\"\u003e\n    \u003cimg src=\"https://github.com/arumes31/rauth/actions/workflows/build.yml/badge.svg?branch=main\" alt=\"Build Status\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/arumes31/rauth/actions/workflows/go-security.yml\"\u003e\n    \u003cimg src=\"https://github.com/arumes31/rauth/actions/workflows/go-security.yml/badge.svg?branch=main\" alt=\"gsec, govul, godep,\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/arumes31/rauth/actions/workflows/security.yml\"\u003e\n    \u003cimg src=\"https://github.com/arumes31/rauth/actions/workflows/security.yml/badge.svg?branch=main\" alt=\"Container Scan\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/arumes31/rauth/stargazers\"\u003e\n    \u003cimg src=\"https://badgen.net/github/stars/arumes31/rauth?color=ea4335\u0026icon=github\u0026label=stars\" alt=\"Stars\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/arumes31/rauth/network/members\"\u003e\n    \u003cimg src=\"https://badgen.net/github/forks/arumes31/rauth?color=ea4335\u0026icon=github\u0026label=forks\" alt=\"Forks\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/arumes31/rauth/issues\"\u003e\n    \u003cimg src=\"https://badgen.net/github/open-issues/arumes31/rauth?color=ea4335\u0026icon=github\u0026label=issues\" alt=\"Issues\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\nRAuth is a lightweight, high-performance authentication proxy and user management system written in **Go**. It is specifically architected to provide a centralized, secure access control layer for self-hosted infrastructure via the Nginx `auth_request` module.\n\nRAuth eliminates the complexity of full-scale identity providers while maintaining enterprise-grade security standards like **Passkey (WebAuthn)** support, **AES-256-GCM** session encryption, and real-time **Prometheus** observability.\n\n## 📖 Table of Contents\n- [🚀 Core Features](#-core-features)\n- [🏗️ System Architecture](#-system-architecture)\n- [🛡️ Security Architecture](#-security-architecture)\n- [📦 Technical Stack](#-technical-stack)\n- [🔧 Nginx Integration](#-nginx-integration)\n- [📊 Monitoring \u0026 Observability](#-monitoring--observability)\n- [📊 Screenshots](#-screenshots)\n- [⚙️ Configuration](#-configuration)\n- [🚀 Deployment](#-deployment)\n- [💻 Development](#-development)\n- [❓ Troubleshooting FAQ](#-troubleshooting-faq)\n\n---\n\n## 🚀 Core Features\n\n\u003ctable width=\"100%\"\u003e\n  \u003ctr\u003e\n    \u003ctd width=\"50%\" valign=\"top\"\u003e\n      \u003ch3\u003e🔐 Multi-Factor Authentication\u003c/h3\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003cb\u003eWebAuthn / Passkeys\u003c/b\u003e: Modern, phishing-resistant authentication using YubiKeys, TouchID, FaceID, or Windows Hello.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eUser-Nameless Login\u003c/b\u003e: Discoverable Credentials support for logging in via hardware key without entering a username.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eTOTP Support\u003c/b\u003e: Fully compatible with Google Authenticator, Authy, and Bitwarden.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eEnforced Setup\u003c/b\u003e: Guided multi-factor enrollment automatically triggered for all new user sign-ins.\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/td\u003e\n    \u003ctd width=\"50%\" valign=\"top\"\u003e\n      \u003ch3\u003e🌐 Smart Session Management\u003c/h3\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003cb\u003eSub-millisecond Validation\u003c/b\u003e: Near-zero latency Go backend cached on a high-speed Redis database.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eAutomatic Token Rotation\u003c/b\u003e: Periodically replaces active session tokens during validation, neutralizing the impact of stolen tokens.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eHardware Binding\u003c/b\u003e: Optionally binds sessions to specific device models using high-entropy User-Agent Client Hints.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eGeo-Fencing\u003c/b\u003e: Real-time MaxMind integration instantly invalidating sessions initiated from anomalous countries.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eSystem-Wide Invalidation\u003c/b\u003e: Automated termination of all active sessions on password changes or 2FA resets.\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd width=\"50%\" valign=\"top\"\u003e\n      \u003ch3\u003e🛠️ Administrative Control\u003c/h3\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003cb\u003eUser Dashboard\u003c/b\u003e: Centralized user provisioning, deletion, and status auditing.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eCredential Resets\u003c/b\u003e: Seamless forced password rotations and 2FA resets.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eAudit Logging\u003c/b\u003e: Secure, structured, and searchable audit feed mapping critical authentication events.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eSecurity Emails\u003c/b\u003e: HTML email alerts notifying users of new logins, resets, and credential changes.\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/td\u003e\n    \u003ctd width=\"50%\" valign=\"top\"\u003e\n      \u003ch3\u003e🎨 Glassmorphism \"Matrix\" UI\u003c/h3\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003cb\u003eVisual Urgency\u003c/b\u003e: Specialized high-intensity red styling for security-critical operations.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eGlassmorphic Layers\u003c/b\u003e: Premium backdrop-filtered frosted-glass modules.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eCyberpunk Styling\u003c/b\u003e: Cyber-glowing iconography and responsive high-performance animations.\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eFull Optimization\u003c/b\u003e: Fully adaptive viewports designed for mobile, tablet, and desktop systems.\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003e [!NOTE]\n\u003e **Browser \u0026 Device Support**: Passkeys are supported on Chrome 67+, Edge 79+, Firefox 60+, and Safari 13+. Hardware keys (YubiKey) work across all platforms, while platform authenticators (TouchID, Windows Hello) require OS-level support.\n\n---\n\n## 🏗️ System Architecture\n\nRAuth integrates seamlessly into your existing Nginx proxy stack using the `auth_request` module.\n\n```mermaid\n%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#dc3545', 'primaryTextColor': '#fff', 'lineColor': '#ef4444', 'nodeBorder': '#dc3545', 'mainBkg': '#1f1f1f', 'actorBkg': '#1f1f1f' }}}%%\ngraph TD\n    User((User)) --\u003e|HTTPS Request| Nginx[Nginx Reverse Proxy]\n    Nginx --\u003e|1. auth_request| RAuth[RAuth Auth Service]\n    RAuth \u003c--\u003e|2. Session Check| Redis[(Redis Store)]\n    RAuth --\u003e|3. Return 200/401| Nginx\n    Nginx --\u003e|4. If 200: Forward| Backend[Your App Backend]\n    Nginx --\u003e|5. If 401: Redirect| Login[RAuth Login UI]\n```\n\n---\n\n## 🛡️ Security Architecture\n\nRAuth is built with a \"Security-First\" mindset, implementing advanced system-wide hardening:\n\n1.  **Authenticated Encryption**: All session tokens stored in cookies are encrypted using **AES-256-GCM**.\n2.  **At-Rest Secret Encryption**: User TOTP secrets are encrypted with the `SERVER_SECRET` before being stored in Redis, protecting against database exposure.\n3.  **Enumeration \u0026 Timing Attack Mitigation**: Uniform response times for login and session checks using fully valid, pre-computed 60-character dummy bcrypt hashes, completely neutralizing username enumeration timing attacks.\n4.  **Brute-Force Protection \u0026 Rate Limiting**: Atomic Redis-backed rate limiting per IP and per username, protecting login pages, registration endpoints, and sensitive profile-level operations (e.g. 2FA verification, password change).\n5.  **Hardened CSRF \u0026 CSP**: Strictly configured CSRF cookies (HTTPOnly, Secure, SameSite=Lax) and a robust Content Security Policy (CSP).\n6.  **Clone Detection**: WebAuthn signature counter persistence allows the detection of cloned or tampered hardware security keys.\n7.  **Hardened Redirects**: Built-in protection against Open Redirects, including protocol-relative URL bypasses.\n8.  **Injection-Safe Emails**: All automated emails are hardened against Header (CRLF) Injection and HTML/XSS attacks.\n9.  **Custom Error Interception**: Branded 404, 403, and 500 error pages prevent technical leakage and provide a unified UX.\n10. **Background Hardening**: Automatic daily Geo-IP database updates and session cleanup tasks.\n\n---\n\n## 📦 Technical Stack\n\n*   **Runtime**: [Go 1.26+](https://golang.org/) (High-concurrency, memory-safe)\n*   **Web Framework**: [Echo v4](https://echo.labstack.com/)\n*   **Identity Store**: [Redis 8.0+](https://redis.io/)\n*   **MFA Core**: [go-webauthn](https://github.com/go-webauthn/webauthn) \u0026 [pquerna/otp](https://github.com/pquerna/otp)\n*   **Geo-IP**: Native Go MMDB integration via [geoip2-golang](https://github.com/oschwald/geoip2-golang)\n*   **Monitoring**: [Prometheus Client](https://github.com/prometheus/client_golang)\n*   **Frontend**: Native Bootstrap 5 with a custom Glassmorphism \"Matrix\" theme.\n\n---\n\n## 🔧 Nginx Integration\n\nRAuth acts as an \"Authorizer\" for Nginx. When a request hits your proxy, Nginx performs a lightweight subrequest to RAuth to verify the user's session.\n\n### Example Nginx Snippet\n\n```nginx\n# 1. Define the RAuth validation endpoint\nlocation = /rauth-verify {\n    internal;\n    proxy_pass http://rauth-auth-service/rauthvalidate;\n    proxy_pass_request_body off;\n    proxy_set_header Content-Length \"\";\n    proxy_set_header X-Original-URI $request_uri;\n    # Forward the real client IP on the subrequest so rauth logs/acts on it\n    # instead of nginx's own address. Pair with TRUST_X_REAL_IP=true (or\n    # TRUST_X_FORWARDED_FOR=true) on the rauth container.\n    proxy_set_header X-Real-IP $remote_addr;\n    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n}\n\n# 2. Protect your application\nlocation / {\n    auth_request /rauth-verify;\n    \n    # Propagate user identity to your backend\n    auth_request_set $user $upstream_http_x_rauth_user;\n    proxy_set_header X-User $user;\n\n    # REQUIRED for Automatic Token Rotation:\n    # Propagate Set-Cookie from RAuth back to the client\n    auth_request_set $new_cookie $upstream_http_set_cookie;\n    add_header Set-Cookie $new_cookie;\n\n    # Handle unauthorized users\n    error_page 401 = @error401;\n    \n    proxy_pass http://your-app-backend;\n}\n\nlocation @error401 {\n    return 302 https://auth.yourdomain.com/rauthlogin?rd=$scheme://$http_host$request_uri;\n}\n```\n\n\u003e [!IMPORTANT]\n\u003e **The RAuth host must NOT be behind `auth_request`.** Serve `auth.yourdomain.com` (its `/rauthlogin`, `/rauthmgmt`, and static asset routes) as a plain `proxy_pass` to RAuth. If the login page itself requires authentication, every unauthenticated visit returns 401 and redirects back to the login page — an infinite loop the user can never escape. Only protect your *other* applications with the `auth_request` subrequest.\n\n### Real Client IP (Cloudflare + direct access)\n\nRAuth's only peer is your proxy (e.g. the Docker bridge `172.x.x.x`), so the real client IP must arrive in a header nginx sets. With no `TRUST_*` flag, RAuth uses **smart mode**, which rejects *private* forwarded IPs — so a direct **LAN** client (`192.168.x`/`10.x`) is dropped and you \"only see the Docker IP\".\n\nIf your service is reachable **both** through Cloudflare **and** directly (LAN/Tailscale bypassing Cloudflare), do **not** set `TRUST_CLOUDFLARE_IP` — RAuth can't distinguish the two paths (its peer is always nginx), so a direct client could spoof `CF-Connecting-IP`. Instead, let nginx resolve the visitor and forward a single authoritative `X-Real-IP`:\n\n```nginx\n# Scope realip to Cloudflare's ranges ONLY (https://www.cloudflare.com/ips/),\n# so direct LAN/Tailscale connections keep their real $remote_addr while\n# Cloudflare traffic is rewritten to the true visitor IP.\nset_real_ip_from 173.245.48.0/20;\nset_real_ip_from 103.21.244.0/22;\n# ... (full IPv4 + IPv6 list from cloudflare.com/ips) ...\nset_real_ip_from 2c0f:f248::/32;\nreal_ip_header CF-Connecting-IP;\nreal_ip_recursive on;\n\n# Forward the resolved client IP to RAuth on the auth subrequest:\n#   proxy_set_header X-Real-IP $remote_addr;\n```\n\nThen set **`TRUST_X_REAL_IP=true`** on the RAuth container. This is safe because nginx overwrites `X-Real-IP` with `$remote_addr`, which a client cannot spoof through the proxy. The full annotated config is in [`nginx-proxy-example.conf`](nginx-proxy-example.conf).\n\n---\n\n## 🔀 Caddy Integration\n\nCaddy features a built-in `forward_auth` directive that makes integrating RAuth incredibly straightforward. \n\n### Example Caddyfile\n\n```caddy\n# Protect app.example.com using RAuth\napp.example.com {\n    # 1. Forward validation request to RAuth's endpoint\n    forward_auth http://rauth-auth-service:5980 {\n        uri /rauthvalidate\n        copy_headers X-RAuth-User\n    }\n\n    # 2. Reverse proxy to your backend service if authorized\n    reverse_proxy http://your-app-backend:8080\n}\n```\n\n---\n\n## 🚦 Traefik Integration\n\nTraefik uses a `ForwardAuth` middleware layer. You define the middleware on the RAuth service and reference it on the router of the application you want to protect.\n\n### Docker Compose Label Example\n\n```yaml\nservices:\n  # 1. Define the RAuth service and middleware labels\n  rauth:\n    image: ghcr.io/arumes31/rauth-auth:latest\n    container_name: rauth-auth-service\n    labels:\n      - \"traefik.http.middlewares.rauth-auth.forwardauth.address=http://rauth-auth-service:5980/rauthvalidate\"\n      - \"traefik.http.middlewares.rauth-auth.forwardauth.trustForwardHeader=true\"\n      - \"traefik.http.middlewares.rauth-auth.forwardauth.authResponseHeaders=X-RAuth-User\"\n\n  # 2. Reference the middleware to protect your application\n  your-app:\n    image: your-app-image:latest\n    labels:\n      - \"traefik.http.routers.your-app.rule=Host(`app.example.com`)\"\n      - \"traefik.http.routers.your-app.middlewares=rauth-auth\"\n```\n\n### 🌐 Proxying Multiple Domains\nRAuth can protect multiple apps across different subdomains using a single deployment. Set your `COOKIE_DOMAIN` to the root domain (e.g., `example.com`) to share session state between `app1.example.com` and `app2.example.com`.\n\n---\n\n## 📊 Monitoring \u0026 Observability\n\nRAuth exposes real-time metrics in Prometheus format at `/metrics`. \n\n### Security \u0026 Usage Metrics\n*   `rauth_login_success_total`: Cumulative count of successful logins.\n*   `rauth_login_failed_total`: Cumulative count of failed attempts.\n*   `rauth_active_sessions`: Gauge showing the current number of valid sessions in Redis.\n*   `rauth_rate_limit_hits_total`: Count of requests blocked by the internal throttler.\n*   `rauth_audit_logs_total`: Counter categorized by action (e.g., `USER_CHANGE_PASSWORD`, `ADMIN_DELETE_USER`).\n\n### Access Control\nBy default, the `/metrics` endpoint is restricted to:\n*   Localhost (`127.0.0.1`)\n*   Private Subnets (`10.0.0.0/8`, etc.)\n*   Tailscale IP ranges (`100.64.0.0/10`)\n\n---\n\n## 📊 Screenshots\n\n\u003cimg width=\"1013\" height=\"1004\" alt=\"grafik\" src=\"https://github.com/user-attachments/assets/6fa18ab8-98fc-457d-821c-a6496f769d38\" /\u003e\n\u003cimg width=\"1522\" height=\"1237\" alt=\"grafik\" src=\"https://github.com/user-attachments/assets/0887e100-f21c-4a97-8b91-cc56f0bac358\" /\u003e\n\u003cimg width=\"465\" height=\"1015\" alt=\"grafik\" src=\"https://github.com/user-attachments/assets/0ccbc5da-6ac3-4883-ab4b-949296e01025\" /\u003e\n\u003cimg width=\"1424\" height=\"547\" alt=\"grafik\" src=\"https://github.com/user-attachments/assets/5031feb7-d2d6-4ea4-b32e-1293ada41399\" /\u003e\n\u003cimg width=\"1440\" height=\"747\" alt=\"grafik\" src=\"https://github.com/user-attachments/assets/3fc67177-9190-495b-99f4-44a72a8acdc3\" /\u003e\n\u003cimg width=\"1498\" height=\"1213\" alt=\"grafik\" src=\"https://github.com/user-attachments/assets/7eb5898b-7bbb-4094-87a2-555cea9cafc4\" /\u003e\n\u003cimg width=\"1423\" height=\"484\" alt=\"grafik\" src=\"https://github.com/user-attachments/assets/af946f2f-a1aa-419d-be78-0e33635f4063\" /\u003e\n\n\n\n---\n\n## ⚙️ Configuration\n\nRAuth is configured via Environment Variables.\n\n\u003cdetails\u003e\n  \u003csummary\u003e🔍 \u003cb\u003eView Configuration Options (Environment Variables)\u003c/b\u003e\u003c/summary\u003e\n\n| Category | Variable | Description | Default |\n|:---|:---|:---|:---|\n| **Secret** | `SERVER_SECRET` | 32+ char key for AES encryption | **REQUIRED** |\n| **Admin**  | `INITIAL_USER` | Initial admin username | `admin` |\n| **Admin**  | `INITIAL_PASSWORD` | Initial admin password | (None) |\n| **Admin**  | `INITIAL_EMAIL` | Initial admin email address | `admin@example.com` |\n| **Admin**  | `INITIAL_2FA_SECRET` | Optional: Pre-set Base32 2FA secret | (None) |\n| **Redis**  | `REDIS_HOST` | Hostname of the Redis instance | `rauth-auth-redis` |\n| **Redis**  | `REDIS_PORT` | Port of the Redis instance | `6379` |\n| **Redis**  | `REDIS_PASSWORD` | Password for Redis auth | (None) |\n| **Auth**   | `COOKIE_DOMAIN` | Domain for the auth cookie | `example.com` |\n| **Auth**   | `ALLOWED_HOSTS` | Redirect whitelist (Comma-separated) | `localhost,127.0.0.1` |\n| **Auth**   | `TOKEN_VALIDITY_MINUTES`| Session duration in minutes | `2880` (2 days) |\n| **Auth**   | `ALLOWED_COUNTRIES` | List of allowed country codes (e.g. `US,DE`) | (Any) |\n| **WebAuthn**| `WEBAUTHN_ORIGINS`| Allowed origins for Passkeys (Comma-separated)| (Auto-generated) |\n| **URL**    | `PUBLIC_URL` | Base URL for email links (e.g., `https://auth.example.com`) | `http://localhost:5980` |\n| **Session**| `TOKEN_ROTATION_MINUTES` | Frequency of automatic session token rotation (0 = disabled) | `0` |\n| **Network**| `AUTH_PORT` | Port to expose the auth service | `5980` |\n| **Proxy**  | `TRUST_X_FORWARDED_FOR` | Trust leftmost IP in `X-Forwarded-For` | `false` |\n| **Proxy**  | `TRUST_X_REAL_IP` | Trust IP in `X-Real-IP` | `false` |\n| **Proxy**  | `TRUST_CLOUDFLARE_IP` | Trust IP in `CF-Connecting-IP` | `false` |\n| **Policy** | `PWD_MIN_LENGTH` | Minimum required password length | `8` |\n| **Policy** | `PWD_REQUIRE_UPPER` | Require uppercase in passwords | `true` |\n| **Policy** | `PWD_REQUIRE_LOWER` | Require lowercase in passwords | `true` |\n| **Policy** | `PWD_REQUIRE_NUMBER` | Require numbers in passwords | `true` |\n| **Policy** | `PWD_REQUIRE_SPECIAL`| Require special chars in passwords | `true` |\n| **Security**| `METRICS_ALLOWED_IPS`| CIDR list for `/metrics` access | (Private + Tailscale) |\n| **Geo-IP** | `MAXMIND_ACCOUNT_ID` | Your Account ID for Geo-IP updates | **REQUIRED** |\n| **Geo-IP** | `MAXMIND_LICENSE_KEY` | Your License Key for Geo-IP updates | **REQUIRED** |\n| **Geo-IP** | `GEOIP_EDITION_IDS` | Databases to download | `GeoLite2-Country` |\n| **Geo-IP** | `MAXMIND_DB_PATH` | Path to local MaxMind .mmdb file | `/app/geoip/GeoLite2-Country.mmdb` |\n| **Geo-IP** | `GEO_API_HOST` | Hostname of local Geo-IP service | `rauth-geo-service` |\n| **Geo-IP** | `GEO_API_PORT` | Port of local Geo-IP service | `3000` |\n| **Email**  | `SMTP_HOST` | SMTP server hostname | (None) |\n| **Email**  | `SMTP_PORT` | SMTP server port (e.g., 587) | `587` |\n| **Email**  | `SMTP_USER` | SMTP username | (None) |\n| **Email**  | `SMTP_PASS` | SMTP password | (None) |\n| **Email**  | `SMTP_FROM` | Sender email address | (None) |\n| **Throttle**| `RATE_LIMIT_LOGIN_MAX`| Max authentication attempts per IP | `30` |\n| **Throttle**| `RATE_LIMIT_LOGIN_DECAY`| Reset window for auth (seconds) | `300` |\n| **Throttle**| `RATE_LIMIT_REG_MAX`| Max registrations per IP | `10` |\n| **Throttle**| `RATE_LIMIT_REG_DECAY`| Reset window for reg (seconds) | `300` |\n| **Throttle**| `RATE_LIMIT_VALIDATE_MAX`| Max validation attempts per IP | `1000` |\n| **Throttle**| `RATE_LIMIT_VALIDATE_DECAY`| Reset window for validation (seconds) | `60` |\n| **Throttle**| `RATE_LIMIT_LOGIN_ACCESS_MAX`| Max GET/POST requests to login per IP | `300` |\n| **Throttle**| `RATE_LIMIT_LOGIN_ACCESS_DECAY`| Reset window for login access (seconds) | `60` |\n| **Throttle**| `RATE_LIMIT_LOGIN_FAIL_USER_MAX`| Max fails per account before lockout | `10` |\n| **Throttle**| `RATE_LIMIT_LOGIN_FAIL_USER_DECAY`| Account lockout duration (seconds) | `300` |\n| **Throttle**| `RATE_LIMIT_LOGIN_FAIL_IP_MAX`| Max global IP failures before block | `50` |\n| **Throttle**| `RATE_LIMIT_LOGIN_FAIL_IP_DECAY`| Global IP block duration (seconds) | `600` |\n| **Regional**| `TZ` | Container Timezone (e.g., `Europe/Berlin`) | `UTC` |\n\n\u003c/details\u003e\n\n---\n\n## 🚀 Deployment \u0026 Setup Guide\n\n### 📋 Prerequisites\n- **Docker** and **Docker Compose** installed on your host system.\n- A registered domain name (e.g. `example.com`) with DNS records pointing to your reverse proxy.\n- An SSL Certificate (WebAuthn / Passkeys require a secure context).\n\n### 🗺️ Step 1: Obtain a Free Geo-IP License (Required)\nRAuth requires MaxMind GeoLite2 databases for advanced session geo-fencing and security logs.\n1. Sign up for a free account at [MaxMind](https://www.maxmind.com/en/geolite2/signup).\n2. Create and generate a **License Key** from the account panel.\n3. Save your **Account ID** and **License Key** to use in the environment config.\n\n### ⚙️ Step 2: Clone and Configure\n1. **Clone the repository**:\n   ```bash\n   git clone https://github.com/arumes31/rauth.git\n   cd rauth\n   ```\n2. **Prepare the environment configuration file**:\n   ```bash\n   cp example.env .env\n   ```\n3. **Configure the `.env` settings**:\n   Open `.env` in an editor and set these minimum required values:\n   ```env\n   # Cryptographic Security Key (generate a random 32+ character string)\n   SERVER_SECRET=your_32char_secure_random_key_here\n\n   # Domain \u0026 Cookie configurations\n   COOKIE_DOMAIN=example.com\n   ALLOWED_HOSTS=auth.example.com,app.example.com\n   PUBLIC_URL=https://auth.example.com\n\n   # Geo-IP credentials\n   MAXMIND_ACCOUNT_ID=your_maxmind_account_id\n   MAXMIND_LICENSE_KEY=your_maxmind_license_key\n\n   # Seed Administrative User\n   INITIAL_USER=admin\n   INITIAL_PASSWORD=temporary_secure_password\n   INITIAL_EMAIL=admin@example.com\n   ```\n\n### 🛡️ Step 4: Configure Reverse Proxy Trust (Recommended)\nBy default, RAuth uses **Smart IP Detection** to find the real client IP if the connection comes from a private network (like Docker). If your reverse proxy is on a public IP or you want explicit control, set these in `.env`:\n- `TRUST_X_FORWARDED_FOR=true`: If your proxy sends the standard `X-Forwarded-For` header.\n- `TRUST_X_REAL_IP=true`: If your proxy sends a single IP in `X-Real-IP`.\n- `TRUST_CLOUDFLARE_IP=true`: If you are using Cloudflare.\n\n### 🚢 Step 5: Choose Deployment Mode \u0026 Launch\n\nRAuth provides two Docker Compose configurations to suit your needs:\n\n#### Option A: Production Deployment (Recommended)\nUse the pre-compiled, official multi-architecture image directly from the GitHub Container Registry (GHCR) with `docker-compose.ghcr.yml`. You do not need to clone the repository or compile anything from source.\n\nCreate a `docker-compose.ghcr.yml` file or run:\n```bash\ndocker compose -f docker-compose.ghcr.yml up -d\n```\n\nHere is the `docker-compose.ghcr.yml` configuration:\n\n```yaml\nservices:\n  rauth-auth-service:\n    image: ghcr.io/arumes31/rauth-auth:latest\n    container_name: rauth-auth-service\n    ports:\n      - \"${AUTH_PORT:-5980}:80\"\n    environment:\n      - REDIS_HOST=rauth-auth-redis\n      - REDIS_PORT=6379\n      - REDIS_PASSWORD=${REDIS_PASSWORD:-rauthsecurepassword}\n      - INITIAL_USER=${INITIAL_USER:-admin}\n      - INITIAL_PASSWORD=${INITIAL_PASSWORD}\n      - INITIAL_EMAIL=${INITIAL_EMAIL:-admin@example.com}\n      - INITIAL_2FA_SECRET=${INITIAL_2FA_SECRET}\n      - TOKEN_VALIDITY_MINUTES=${TOKEN_VALIDITY_MINUTES:-2880}\n      - COOKIE_DOMAIN=${COOKIE_DOMAIN:-example.com}\n      - ALLOWED_HOSTS=${ALLOWED_HOSTS:-localhost,127.0.0.1}\n      - ALLOWED_COUNTRIES=${ALLOWED_COUNTRIES}\n      - SERVER_SECRET=${SERVER_SECRET}\n      - TZ=${TZ:-UTC}\n      - MAXMIND_ACCOUNT_ID=${MAXMIND_ACCOUNT_ID}\n      - MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY}\n      - MAXMIND_DB_PATH=/app/geoip/GeoLite2-Country.mmdb\n      - GEOIP_EDITION_IDS=${GEOIP_EDITION_IDS:-GeoLite2-Country}\n      - GEOIP_DATABASE_DIRECTORY=/app/geoip\n      - WEBAUTHN_ORIGINS=${WEBAUTHN_ORIGINS}\n      - PUBLIC_URL=${PUBLIC_URL:-http://localhost:5980}\n      - PWD_MIN_LENGTH=${PWD_MIN_LENGTH:-8}\n      - PWD_REQUIRE_UPPER=${PWD_REQUIRE_UPPER:-true}\n      - PWD_REQUIRE_LOWER=${PWD_REQUIRE_LOWER:-true}\n      - PWD_REQUIRE_NUMBER=${PWD_REQUIRE_NUMBER:-true}\n      - PWD_REQUIRE_SPECIAL=${PWD_REQUIRE_SPECIAL:-true}\n      - METRICS_ALLOWED_IPS=${METRICS_ALLOWED_IPS:-127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10}\n      - RATE_LIMIT_LOGIN_MAX=${RATE_LIMIT_LOGIN_MAX:-30}\n      - RATE_LIMIT_LOGIN_DECAY=${RATE_LIMIT_LOGIN_DECAY:-300}\n      - RATE_LIMIT_REG_MAX=${RATE_LIMIT_REG_MAX:-10}\n      - RATE_LIMIT_REG_DECAY=${RATE_LIMIT_REG_DECAY:-300}\n      - RATE_LIMIT_VALIDATE_MAX=${RATE_LIMIT_VALIDATE_MAX:-1000}\n      - RATE_LIMIT_VALIDATE_DECAY=${RATE_LIMIT_VALIDATE_DECAY:-60}\n      - RATE_LIMIT_LOGIN_ACCESS_MAX=${RATE_LIMIT_LOGIN_ACCESS_MAX:-300}\n      - RATE_LIMIT_LOGIN_ACCESS_DECAY=${RATE_LIMIT_LOGIN_ACCESS_DECAY:-60}\n      - RATE_LIMIT_LOGIN_FAIL_USER_MAX=${RATE_LIMIT_LOGIN_FAIL_USER_MAX:-10}\n      - RATE_LIMIT_LOGIN_FAIL_USER_DECAY=${RATE_LIMIT_LOGIN_FAIL_USER_DECAY:-300}\n      - RATE_LIMIT_LOGIN_FAIL_IP_MAX=${RATE_LIMIT_LOGIN_FAIL_IP_MAX:-50}\n      - RATE_LIMIT_LOGIN_FAIL_IP_DECAY=${RATE_LIMIT_LOGIN_FAIL_IP_DECAY:-600}\n      - SMTP_HOST=${SMTP_HOST}\n      - SMTP_PORT=${SMTP_PORT:-587}\n      - SMTP_USER=${SMTP_USER}\n      - SMTP_PASS=${SMTP_PASS}\n      - SMTP_FROM=${SMTP_FROM}\n    depends_on:\n      - rauth-auth-redis\n    volumes:\n      - ./geoip-data:/app/geoip\n    networks:\n      - auth-network\n\n  rauth-auth-redis:\n    image: redis:7.4-alpine\n    container_name: rauth-auth-redis\n    hostname: rauth-auth-redis\n    command: redis-server --requirepass ${REDIS_PASSWORD:-rauthsecurepassword}\n    volumes:\n      - ./redis-data:/data\n    networks:\n      - auth-network\n\nnetworks:\n  auth-network:\n    driver: bridge\n```\n\n#### Option B: Local Development / Build from Source\nIf you are modifying the codebase or prefer to compile the application locally, use `docker-compose.yml` (which includes a local build context):\n\n```bash\ndocker compose up -d --build\n```\nRAuth will automatically pull the secure image from GHCR, download and update the latest Geo-IP database, boot the Redis memory store, and seed your initial admin user.\n\n### 🔐 Step 4: Access the Admin Dashboard \u0026 Secure Account\n1. Open your browser and navigate to RAuth's administration portal at `https://auth.example.com/rauthmgmt` (or `http://localhost:5980/rauthmgmt` for local development).\n2. Authenticate using your `INITIAL_USER` and `INITIAL_PASSWORD`.\n3. Follow the secure prompt to register your Multi-Factor Authentication keys (Passkey / TOTP) to fully secure the admin account.\n\n### 🔀 Step 5: Configure Your Reverse Proxy (Nginx, Caddy, or Traefik)\nTo start protecting your applications, configure your reverse proxy to forward user verification requests to RAuth's `/rauthvalidate` endpoint:\n- **Nginx**: See the [Nginx Integration](#-nginx-integration) section to set up the `auth_request` subrequest logic.\n- **Caddy**: See the [Caddy Integration](#-caddy-integration) section to configure Caddy's built-in `forward_auth` block.\n- **Traefik**: See the [Traefik Integration](#-traefik-integration) section to enable the `ForwardAuth` middleware layer.\n\n---\n\n## 💻 Development\n\n### Prerequisites\n*   Go 1.26+\n*   Redis (or [miniredis](https://github.com/alicebob/miniredis) for testing)\n\n### Testing\nWe use a combination of unit tests, integration tests, and fuzzing to ensure core security logic remains robust.\n```bash\ngo test -v ./...\n```\n\n---\n\n## ❓ Troubleshooting FAQ\n\n\u003cdetails\u003e\n  \u003csummary\u003e❓ \u003cb\u003eView Troubleshooting FAQ Solutions\u003c/b\u003e\u003c/summary\u003e\n\n### 🌐 Session \u0026 Cookie Routing\n**Q: Why am I stuck in a 401 Redirect Loop?**  \nA: There are two common causes:\n\n1. **Cookie domain mismatch** — the `COOKIE_DOMAIN` in RAuth doesn't match the domain of the application you are protecting. Ensure the cookie domain is set to a common root domain (e.g. `example.com`) to allow cookie sharing across subdomains (e.g. `app1.example.com` and `auth.example.com`).\n2. **The login page itself is behind auth** — the RAuth host (e.g. `auth.yourdomain.com`) and its `/rauthlogin`, `/rauthmgmt`, and static asset routes **must be served directly and reachable WITHOUT an `auth_request`**. If you place the auth domain behind RAuth's own `auth_request`, every visit to the login page returns 401 and redirects back to the login page — an infinite loop, since the user can never reach the form to authenticate. Expose `auth.yourdomain.com` as a plain reverse-proxy `proxy_pass` to RAuth (no `auth_request`), and only protect your *other* applications with the subrequest.\n\n**Q: Why is my session immediately invalidated when using a tablet or rotating my device?**  \nA: Large tablets (like the Samsung Galaxy Tab series) default to requesting the \"Desktop site\" (spoofing a desktop Linux UA) but dynamically switch back to a mobile UA when rotated, resized in split-screen/pop-up views, or during background/Service-Worker requests. Using **User-Agent Client Hints (UA-CH)** under secure contexts (HTTPS) and the lenient browser-engine fallback for other browsers (like Safari/Firefox) and non-secure contexts reduce false invalidations but cannot eliminate them across all device mode, rotation, split-screen, or background/request context changes.\n\n---\n\n### 🔑 Passkeys \u0026 WebAuthn\n**Q: WebAuthn/Passkey registration fails or gets rejected?**  \nA: WebAuthn requires a secure origin (HTTPS or `localhost` for development). Ensure your Nginx proxy is serving over SSL and forwarding the correct host headers (`proxy_set_header Host $host;`). Additionally, check that `WEBAUTHN_ORIGINS` is configured with the exact origin scheme and port (e.g., `https://auth.example.com`).\n\n**Q: How do I resolve signature counter/verification mismatches?**  \nA: This can happen if the hardware key was cloned or the local state fell out of sync. For security reasons, RAuth detects this as a potential clone attack. Reset the user's WebAuthn key in the administrative dashboard to register the device afresh.\n\n---\n\n### 🛡️ Geo-IP \u0026 Blocking Policies\n**Q: Why am I getting \"403 Forbidden\" geo-blocking errors for legitimate requests?**  \nA: This happens if the user's IP address maps to an unlisted country, or the local MaxMind database is stale or missing. Verify that `MAXMIND_ACCOUNT_ID` and `MAXMIND_LICENSE_KEY` are correct, check container logs for geo-download status, or adjust the `ALLOWED_COUNTRIES` environment variable.\n\n**Q: Why are active sessions not displaying the correct client IP addresses in the admin audits?**  \nA: Ensure your Nginx configuration passes `proxy_set_header X-Real-IP $remote_addr;` and `proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;` to RAuth. RAuth includes **Smart IP Detection** which automatically trusts these headers if the immediate connection originates from a private IP range (like a Docker network). For complex multi-hop setups or if your proxy is on a public IP, you must explicitly enable `TRUST_X_FORWARDED_FOR=true` in your `.env`.\n\n---\n\n### ⚡ Infrastructure \u0026 Networking\n**Q: How does Automatic Token Rotation work and what are the requirements?**  \nA: When `TOKEN_ROTATION_MINUTES` is set, RAuth will automatically issue a new session token periodically during the background validation subrequest. This significantly reduces the risk of session hijacking. **Requirement:** Your reverse proxy (Nginx/Traefik) must be configured to forward the `Set-Cookie` header from the auth subrequest back to the client.\n\n**Q: Why are users experiencing \"Rate Limit Exceeded\" blockages during normal dashboard use?**  \nA: The default session validation threshold might be too aggressive for heavy single-page apps making hundreds of concurrent resource subrequests. Increase `RATE_LIMIT_VALIDATE_MAX` (e.g. to `5000`) or adjust the decay window `RATE_LIMIT_VALIDATE_DECAY` to accommodate high-volume internal reverse-proxied traffic.\n\n**Q: \"Redis connection refused\" in Docker?**  \nA: Ensure RAuth and Redis are on the same Docker network. If using the default Compose file, use `REDIS_HOST=rauth-auth-redis`.\n\n**Q: How do I resolve \"SMTP connection timed out\" or authentication errors for security emails?**  \nA: Ensure RAuth's container can reach the SMTP host (check DNS and outbound firewall rules). Common ports are `587` (StartTLS) or `465` (SSL). Verify `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, and `SMTP_PASS` are set correctly.\n\n---\n\n### 🔑 Recovery, Metrics, \u0026 Local Testing\n**Q: How do I recover if the administrator loses their 2FA / TOTP key or gets locked out?**  \nA: You can easily boot RAuth in a recovery mode. Set `INITIAL_USER`, `INITIAL_PASSWORD`, and `INITIAL_EMAIL` environment variables inside your `.env` file and restart the container. On startup, RAuth automatically verifies if the admin credentials are valid or updates them. Alternatively, if you have access to your Redis CLI, you can manually delete the admin's WebAuthn key using:\n```bash\nredis-cli DEL user:admin:webauthn_creds\n```\n\n**Q: Why is Prometheus unable to scrape metrics from the `/metrics` endpoint?**  \nA: By default, RAuth restricts metrics access to localhost and private network CIDR blocks for security. If your Prometheus instance runs on an external network, add its IP address or subnet range to the `METRICS_ALLOWED_IPS` environment variable (e.g. `METRICS_ALLOWED_IPS=127.0.0.1,192.168.1.150`).\n\n**Q: Why is the authentication cookie not saving when testing in local non-HTTPS development?**  \nA: Modern browsers enforce the `Secure` attribute on cookies and block them over unencrypted HTTP links. However, browsers treat `localhost` and `127.0.0.1` as secure contexts even over plain HTTP. For local testing without SSL, ensure your browser URL points to `http://localhost:5980` instead of a custom local domain (e.g. `http://auth.local`) or configure an SSL reverse proxy.\n\n\u003c/details\u003e\n\n\u003c/details\u003e\n\n---\nBuilt with ❤️ for secure, fast, and private self-hosting.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farumes31%2Frauth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farumes31%2Frauth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farumes31%2Frauth/lists"}