Skip to content

About

A serverless progressive web application that generates TOTP and HOTP codes, enabling robust two-factor authentication (2FA) directly within your browser. Powered by Nuxt 4, NuxtUI 4, Cloudflare Pages, Vercel and Upstash Redis.

Topics

Resources

Stars

73 stars

Watchers

1 watching

Forks

Repository files navigation

MyAuthenticator logo

MyAuthenticator

A simple 2FA authenticator you can host yourself.
Keep your codes in your browser, sign in with a passkey, and use them offline.

Built with Nuxt 4 OTP secrets encrypted with AES-256-GCM Progressive web app with offline access

Try Live Demo    Deploy Your Own

Public demo password: Admin@123$

Features · Screenshots · Deployment · Self-host · Local dev · Security · Troubleshooting


Features

  • ⏱️ TOTP and HOTP codes. Supports time-based and counter-based codes, with settings for digits, periods, and algorithms.
  • 📴 Offline access. Once your accounts are cached, you can unlock them and get codes without an internet connection.
  • 🔐 Browser-side encryption. Your OTP secrets are encrypted with AES-256-GCM before they're saved to the server.
  • 🔑 Passkeys. Sign in with your device or a security key. Passkeys with PRF support can unlock the vault too.
  • 📷 QR scanning. Add accounts using your camera, a QR image, or a secret entered by hand.
  • 💾 Backups. Export a password-protected backup, or move accounts using plain otpauth:// URI lists.
  • 🎨 Themes. Pick light or dark mode, change colors and fonts, and add service icons.
  • 📱 App installation. Add it to your home screen or install it through a supported browser.

Built with Nuxt 4, Nuxt UI, Tailwind CSS, TypeScript, and Redis.

Screenshots

MyAuthenticator vault unlock screen Authenticator dashboard showing account codes Add authenticator screen

Settings and navigation menu Theme customization options Backup and restore options

Deploy your own

Deploy MyAuthenticator to the cloud or run it on your own server. Pick your preferred platform to get started.

Important

Fork this repository before deploying. Cloud deployments must be built from your own fork so you can pull in future updates.

Fork this repository

Deploy to Cloudflare Pages    Deploy with Vercel    Self-Host with Node.js

Each instance manages its own encrypted vault and connects to a Redis database.

You'll need:

  • A GitHub account to fork this repository.
  • An Upstash account for Redis (or local Redis for self-hosting).
  • An account for your target host: Cloudflare Pages, Vercel, or your own machine.

1. Prepare your credentials

You'll use the same three values with either host.

Create a Redis database

  1. Open the Upstash console and create a Redis database.
  2. Open the database's REST API section.
  3. Copy the REST URL and REST token. Choose the read/write token so the app can save your accounts.

Generate a session secret

Run this in a terminal and copy the output:

openssl rand -hex 32
Don't have OpenSSL? Use Node.js instead
node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))"

Environment variables

Both hosts need the same three variables. Paste each value without quotes:

  • NUXT_SESSION_PASSWORD — the random string you just generated. It must be at least 32 characters and is used to protect session cookies.
  • UPSTASH_REDIS_REST_URL — the HTTPS REST URL from your database, such as https://your-database.upstash.io.
  • UPSTASH_REDIS_REST_TOKEN — the read/write REST token from the same database.

How you enter them depends on the host:

  • Cloudflare Pages — add them in Settings → Environment variables (for both Production and Preview).
  • Vercel — add them in Project Settings → Environment Variables.

Both platform sections list the exact names to copy.

The session secret stays in your server settings. You'll choose a separate password for opening your vault when you first use the app.

2. Choose your host

Cloudflare Pages

Fork this repository Connect to Pages

  1. Fork this repository using the button above. Keep it as your own fork so you can pull updates later.

  2. Sign in to Cloudflare and go to Workers & Pages.

  3. Select Create application → Pages → Connect to Git.

  4. Connect your GitHub account and select your fork. Choose the branch to build from (usually main).

  5. Cloudflare detects Nuxt and fills in the build command and build output directory for you. Keep the detected values — you don't need to enter them by hand.

  6. Add the three environment variables in Settings → Environment variables. Add them for both Production and Preview — use these exact names:

    NUXT_SESSION_PASSWORD
    UPSTASH_REDIS_REST_URL
    UPSTASH_REDIS_REST_TOKEN
    

    Paste the corresponding value for each, then save.

  7. Select Save and Deploy. When the build finishes, open the <your-project>.pages.dev link and create your vault.

Every push to your fork — including when you sync it with this repository — triggers a new Pages deployment automatically.

Note

Cloudflare Pages runs the app on its Functions runtime. Use your Upstash REST credentials there; a TCP REDIS_URL isn't supported.

Vercel

Fork this repository Import to Vercel

  1. Fork this repository using the button above. Keep it as your own fork so you can pull updates later.

  2. In Vercel, select Add New → Project and import your fork from GitHub.

  3. Use the repository root as the root directory and keep the detected Nuxt framework preset.

  4. Add the three environment variables in Project Settings → Environment Variables. Vercel does not pre-fill the names, so add each one — use these exact names:

    NUXT_SESSION_PASSWORD
    UPSTASH_REDIS_REST_URL
    UPSTASH_REDIS_REST_TOKEN
    

    Paste the corresponding value for each. See step 1 if you haven't created them yet.

  5. Select Deploy. When it finishes, open the vercel.app link and create your vault.

Nuxt detects Vercel automatically. If you change environment variables later, redeploy to pick up the new values.

Every push to your fork — including when you sync it with this repository — triggers a new deployment automatically.

Keeping your deployment updated

Because your deployment builds from your fork, you can pull in new releases, dependency updates, and security fixes at any time:

  1. Open your fork on GitHub.
  2. Above the file list, select Sync fork → Update branch.
  3. Cloudflare Pages or Vercel detects the new commits and redeploys automatically.

Self-Host (Node.js)

Deploy and run MyAuthenticator as a production Node.js service on your own Linux server, VPS, or private network.

  1. Clone and install dependencies

    git clone https://github.com/IAMSDR/MyAuthenticator.git
    cd MyAuthenticator
    pnpm install --frozen-lockfile
  2. Configure your environment

    cp .env.example .env

    Open .env and fill in:

    • NUXT_SESSION_PASSWORD — your random 32+ character string from step 1.
    • Redis database: set REDIS_URL="redis://localhost:6379" for local Redis, or your UPSTASH_REDIS_REST_* credentials.
    • PORT — server port. Uncomment the PORT line in .env and set it to 3000 if you want to use the address below.
  3. Build for production

    pnpm build

    This compiles the optimized production server to .output/server/index.mjs.

  4. Start the production server

    node .output/server/index.mjs

    Or run a local preview that automatically loads .env: pnpm preview.

  5. When the server starts, open your app's address (or reverse proxy domain) and create your vault.

Keep running in the background with PM2 or systemd

Using PM2

pnpm dlx pm2 start .output/server/index.mjs --name myauthenticator
pnpm dlx pm2 save
pnpm dlx pm2 startup

Using systemd

Create /etc/systemd/system/myauthenticator.service:

[Unit]
Description=MyAuthenticator 2FA Service
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/path/to/MyAuthenticator
ExecStart=/usr/bin/node /path/to/MyAuthenticator/.output/server/index.mjs
Restart=always
EnvironmentFile=/path/to/MyAuthenticator/.env

[Install]
WantedBy=multi-user.target

Then reload and enable:

sudo systemctl daemon-reload
sudo systemctl enable --now myauthenticator
Reverse proxy & HTTPS setup (Nginx / Caddy)

Browsers require a secure context (HTTPS) for WebAuthn passkeys, camera QR scanning, and client-side encryption whenever you access the app outside localhost.

Caddy (automatic SSL)

auth.yourdomain.com {
    reverse_proxy localhost:3000
}

Nginx

server {
    server_name auth.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

3. Create your vault

  1. Open your app's HTTPS link. On a fresh database, you'll see Setup your account.
  2. Choose a vault password, repeat it, and follow the confirmation prompt.
  3. Add your first account by scanning its 2FA QR code, uploading a QR image, or entering the secret manually.

You can now use that password to open the vault on your other devices too.

  • To add a passkey, open Passkeys in the menu.
  • To save an encrypted backup, open Backup & Restore and choose a separate backup password.
  • To install the app, use your browser's Install or Add to home screen option.

Local development

Set up a local development environment with hot-reloading if you want to develop or contribute to the project:

Before you start, you'll need:

  • Git and Node.js 22. The pinned Node.js version is in .nvmrc.
  • pnpm 9.15.1.
  • A Redis database, either local or through Upstash.

If pnpm is not installed, run npm install -g pnpm@9.15.1 after installing Node.js.

1. Clone and install

git clone https://github.com/IAMSDR/MyAuthenticator.git
cd MyAuthenticator
pnpm install

2. Configure your environment

cp .env.example .env

Open .env and fill in your session secret and Upstash credentials. Add PORT=3000 if you want to use the address below.

Using local Redis?

With Redis running locally, remove both UPSTASH_REDIS_REST_* entries from .env and set:

REDIS_URL="redis://localhost:6379"

Keep NUXT_SESSION_PASSWORD and PORT. If both Upstash credentials and REDIS_URL are set, the app chooses Upstash.

This works with the Node.js server. On Cloudflare Pages, use Upstash's REST credentials.

3. Start the dev server

pnpm dev

Open http://localhost:3000 and create your vault. Use localhost rather than a local network IP so browser encryption and passkeys can work without HTTPS.

Build commands and optional settings

Commands

  • pnpm build builds for Node.js locally, or for the hosting platform Nuxt detects.
  • pnpm preview runs a local preview after pnpm build and loads your .env file.
  • pnpm build:cloudflare builds the Cloudflare output used by Cloudflare Pages.
  • pnpm lint runs ESLint.

Optional environment variables

  • REDIS_URL sets the Redis TCP connection string for Node.js, if you're not using Upstash.
  • PORT changes the local server port. The example .env uses 3000.
  • NITRO_PORT overrides the port for the production server or pnpm preview.

Offline access

Open and unlock the app while you're online at least once on each device. This lets the browser save the app, your account data with encrypted OTP secrets, and a password-encrypted copy of the vault key.

  • You can then unlock your saved accounts with your vault password and get codes offline.
  • Adding, editing, deleting, or restoring accounts needs a connection, as does managing authentication settings.
  • When you're back online, the app refreshes your account data and checks for updates.

If you clear your browser's data or switch to a new device, you'll need to open the app online again first.

How security works

Here's what the app encrypts and what the server stores:

  • OTP secrets are encrypted and decrypted in your browser using AES-256-GCM.
  • The vault key is created in the browser and saved only in encrypted form. While the vault is open, the unlocked key stays in browser memory.
  • Your vault password is sent over HTTPS during setup and password login. The server stores a bcrypt hash to check login attempts.
  • Account details such as labels, issuers, icons, and OTP settings are stored without vault encryption.
  • Encrypted backups protect the exported account data with the backup password you choose. Plain URI exports aren't encrypted.
Encryption keys and passkey compatibility
  • During setup, the browser creates a random 256-bit data encryption key (DEK).
  • Your password is used with PBKDF2 to derive another key that encrypts the DEK. This is called key wrapping, and it also makes offline unlocking possible.
  • A passkey with WebAuthn PRF support can wrap and unlock the DEK too. Without PRF support, the passkey can sign you in, but you'll still need your password to open the vault.
  • Locking the vault, signing out, or leaving the page clears the unlocked key.

Troubleshooting

  • “Redis not configured” or setup won't finish

    Check that the REST URL and token come from the same Upstash database and that the token has write access. Make sure both values are saved in your host's environment settings.

  • A session password error

    Check that NUXT_SESSION_PASSWORD is at least 32 characters. Restart the local server or redeploy after changing it.

  • Cloudflare Pages build or Redis TCP errors

    Cloudflare Pages builds the app with its Cloudflare runtime preset. Use Upstash REST credentials for the database there — a TCP REDIS_URL isn't supported on Pages.

  • Camera, passkeys, or encryption aren't working

    Open the app over HTTPS, or use localhost during development. Also check browser support and camera permissions.

  • Your passkey signs in, but the app still asks for a password

    Your browser or authenticator may not support PRF. Use your vault password to unlock it.

  • You can't unlock offline

    Open and unlock the app online in the same browser first so it can save the data it needs.

  • The local app uses a different port

    Check PORT in .env. The terminal output from pnpm dev will show the address to open.

Contributing

Found a bug or have an idea? Open an issue or send a pull request. If something's broken, include your browser, where you're hosting the app, and the steps to reproduce it.

License

GNU Affero General Public License v3.0 (AGPL-3.0)

Copyright (C) 2025 IAMSDR. This program is free software: you can redistribute it and/or modify it under the terms of the AGPL-3.0. Because it is licensed under the AGPL, anyone who runs a modified version as a network service must also make their source available under the same license.

About

A serverless progressive web application that generates TOTP and HOTP codes, enabling robust two-factor authentication (2FA) directly within your browser. Powered by Nuxt 4, NuxtUI 4, Cloudflare Pages, Vercel and Upstash Redis.

Topics

Resources

Stars

73 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages