A simple 2FA authenticator you can host yourself.
Keep your codes in your browser, sign in with a passkey, and use them offline.
Public demo password: Admin@123$
Features · Screenshots · Deployment · Self-host · Local dev · Security · Troubleshooting
- ⏱️ 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.
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.
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.
You'll use the same three values with either host.
Create a Redis database
- Open the Upstash console and create a Redis database.
- Open the database's REST API section.
- 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 32Don't have OpenSSL? Use Node.js instead
node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))"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 ashttps://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.
-
Fork this repository using the button above. Keep it as your own fork so you can pull updates later.
-
Sign in to Cloudflare and go to Workers & Pages.
-
Select Create application → Pages → Connect to Git.
-
Connect your GitHub account and select your fork. Choose the branch to build from (usually
main). -
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.
-
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_TOKENPaste the corresponding value for each, then save.
-
Select Save and Deploy. When the build finishes, open the
<your-project>.pages.devlink 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.
-
Fork this repository using the button above. Keep it as your own fork so you can pull updates later.
-
In Vercel, select Add New → Project and import your fork from GitHub.
-
Use the repository root as the root directory and keep the detected Nuxt framework preset.
-
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_TOKENPaste the corresponding value for each. See step 1 if you haven't created them yet.
-
Select Deploy. When it finishes, open the
vercel.applink 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.
Because your deployment builds from your fork, you can pull in new releases, dependency updates, and security fixes at any time:
- Open your fork on GitHub.
- Above the file list, select Sync fork → Update branch.
- Cloudflare Pages or Vercel detects the new commits and redeploys automatically.
Deploy and run MyAuthenticator as a production Node.js service on your own Linux server, VPS, or private network.
-
Clone and install dependencies
git clone https://github.com/IAMSDR/MyAuthenticator.git cd MyAuthenticator pnpm install --frozen-lockfile -
Configure your environment
cp .env.example .env
Open
.envand 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 yourUPSTASH_REDIS_REST_*credentials. PORT— server port. Uncomment thePORTline in.envand set it to3000if you want to use the address below.
-
Build for production
pnpm build
This compiles the optimized production server to
.output/server/index.mjs. -
Start the production server
node .output/server/index.mjs
Or run a local preview that automatically loads
.env:pnpm preview. -
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 startupUsing 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.targetThen reload and enable:
sudo systemctl daemon-reload
sudo systemctl enable --now myauthenticatorReverse 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;
}
}- Open your app's HTTPS link. On a fresh database, you'll see Setup your account.
- Choose a vault password, repeat it, and follow the confirmation prompt.
- 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.
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 install2. Configure your environment
cp .env.example .envOpen .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 devOpen 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 buildbuilds for Node.js locally, or for the hosting platform Nuxt detects.pnpm previewruns a local preview afterpnpm buildand loads your.envfile.pnpm build:cloudflarebuilds the Cloudflare output used by Cloudflare Pages.pnpm lintruns ESLint.
Optional environment variables
REDIS_URLsets the Redis TCP connection string for Node.js, if you're not using Upstash.PORTchanges the local server port. The example.envuses3000.NITRO_PORToverrides the port for the production server orpnpm preview.
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.
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.
-
“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_PASSWORDis 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_URLisn't supported on Pages. -
Camera, passkeys, or encryption aren't working
Open the app over HTTPS, or use
localhostduring 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
PORTin.env. The terminal output frompnpm devwill show the address to open.
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.
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.






