Security & Trust
How TeamsMFA keeps your team's MFA codes safe.
You're trusting us with the second factor for your most sensitive accounts. This page explains how seeds are encrypted, how people sign in, how sharing and revocation work, and how to report a problem. It's written in enough detail for a security reviewer to check.
Last updated November 12, 2026 · security.txt
Certifications
Where we stand today
TeamsMFA does not hold a SOC 2 or ISO 27001 certification, and has not had a third-party penetration test. It's a young product built by a small team. Rather than show a badge we haven't earned, we describe exactly how the product works below.
Each section names the algorithms, lifetimes and checks the code actually uses, so your security team can judge it on the details. If anything on this page turns out to be wrong, please tell us.
For a security questionnaire, a DPA, or a call with the engineer who wrote the vault, email security@teamsmfa.com. A person reads every message.
- Yes Seeds encrypted per team, never sent to browsers
- Yes Phishing-resistant passkeys (WebAuthn)
- Yes Per-team audit log of every code view
- Yes Dedicated tenants with their own database and key
- Yes Vulnerability disclosure with safe harbor
- Not yet SOC 2 Type 1 or Type 2
- Not yet ISO/IEC 27001
- Not yet Third-party penetration test
- Not yet SAML/OIDC single sign-on
- Not yet Audit log export
How we protect your data
The details a security review asks for
Encryption
- Every MFA seed is encrypted with AES-256-GCM before it's stored. Each seal uses a fresh random 96-bit IV. The stored value is just the IV and ciphertext. The plaintext seed is never written to the database or to logs.
- Each team has its own key. Keys are derived with HKDF-SHA-256 from the tenant's master key, using the team's id as context. Ciphertext from one team won't decrypt under another team's key, even within the same database.
- Each ciphertext is bound to its row. The seed's own id is included as authenticated data, so copying an encrypted seed into a different row makes decryption fail instead of returning someone else's secret.
- The master key is separate from the data. It's stored as an encrypted Cloudflare Worker secret, not in the database. A copy of the database alone can't recover a single seed.
- Codes are computed on the server. When someone opens a code, the server decrypts the seed in memory, generates the current RFC 6238 code (SHA-1, SHA-256 or SHA-512; 6 or 8 digits), and returns only that code. No API endpoint returns a seed.
- QR codes are read on your device. When you scan a setup QR code, your browser decodes it locally. Camera frames are never uploaded. Only the decoded key is sent to us, over HTTPS, and only once, when the code is added.
- In transit: HTTPS only, with HSTS for two years (including subdomains, preload-eligible). Cloudflare issues and rotates the TLS certificates.
- At rest, below our layer: Cloudflare D1, which holds account data, also encrypts its storage.
Sign-in and sessions
- There are no passwords. You sign in with a link emailed to you. Each link carries 256 bits of randomness, expires in 15 minutes, and works once. We store only its SHA-256 hash.
- Links are protected against email scanners and server logs. The token sits in the URL fragment, which browsers never send to servers. The page then submits it with a POST request, so a mail scanner that prefetches links can't use it up or redeem it.
- The sign-in form doesn't reveal who has an account. It responds the same way for every address. It's also rate-limited per email address and per IP.
- Passkeys add a phishing-resistant second step. Once you register a passkey (Face ID, Touch ID, Windows Hello, a security key or a password manager), every new session must use it before reaching any team data. Passkeys are bound to teamsmfa.com and require user verification. Each challenge is single-use and expires in five minutes. Dedicated tenants can make passkeys mandatory for everyone.
- Sessions are stored server-side. Your browser holds a random 256-bit token in a
__Host-cookie (HttpOnly, Secure, SameSite=Lax). We store only its hash. Sessions last 14 days and can be ended individually or everywhere at once. - Requests that change data are protected against cross-site forgery. They must be same-origin JSON. The API answers no cross-origin preflight requests, so another site can't submit changes on your behalf.
- The browser is locked down. Our Content Security Policy allows scripts, styles and connections only from teamsmfa.com, with no inline scripts and no third-party code. The site also blocks being framed, sends no referrer, and sets
nosniff.
Revocation and offboarding
- Removing someone takes effect immediately. Membership is checked on every request, so anything they ask for afterwards, including a code, gets a 404, as if the team doesn't exist. Nothing needs to expire first. A code already on their screen stays valid only until it expires, usually within a minute.
- Viewers never had the seed. With a shared authenticator app, you'd have to trust a former employee to delete it. Here, a removed viewer only ever saw short-lived codes, so you don't need to re-enroll MFA on the underlying account.
- Owners who added a code did see its seed once, because they scanned or typed it in. If an owner who added codes leaves on bad terms, re-enroll MFA on those accounts. The audit log shows who added each code.
- Lost device: any team owner can reset a member's passkeys. This deletes all their passkeys and ends every session they have, on every device. The member is emailed, and the reset is logged. Owners can't reset their own passkeys, so that request has to come from another owner.
- Sign out everywhere ends every session for your account. It requires a passkey-verified session, so someone who got in with only an email link can't use it to lock you out.
- Deleting a code or a team permanently removes the encrypted seeds from the database, and the deletion is logged.
How code reaches production
- Every change goes through a pull request. Unit tests (TOTP vectors, vault round-trips) and an end-to-end API test against a real local instance run on every one. Production deploys run automatically, and only after those checks pass on
main. - There are no servers to patch. The app runs as a Cloudflare Worker in V8 isolates. We have no SSH ports and no operating systems to maintain.
- Secrets are kept out of the code. They live in Cloudflare Worker secrets and GitHub Actions secrets, never in source control or in anything sent to the browser.
- Backups: Cloudflare D1 supports point-in-time restore. We don't publish formal RTO/RPO targets or an uptime SLA yet.
Privacy
- There's one cookie: the session cookie set when you sign in. There are no analytics, advertising trackers or third-party scripts on teamsmfa.com.
- We don't use AI on your data. TeamsMFA has no machine-learning features and no AI subprocessors.
- We don't sell personal information. Email privacy@teamsmfa.com to export or delete your account data. Details are in our Privacy Policy.
- We make no data-residency promise yet. Data is stored in Cloudflare D1 and served from Cloudflare's global network. If you need data kept in a specific region, tell us before you buy.
Subprocessors
Who else can touch your data
This is the complete list. We'll update this page, and the date at the top, before adding any subprocessor that handles customer data.
Cloudflare, Inc.
Compute (Workers), database (D1), CDN, DNS, DDoS protection, and delivery of sign-in and invitation emails.
- Data
- Account data, encrypted seeds, audit log, and traffic to the service.
- Location
- United States, global edge network
GitHub, Inc.
Source code and continuous integration.
- Data
- None. No customer data is stored in or sent to GitHub.
- Location
- United States
Mail sent to our published addresses lands on a mail server run by TeamsMFA's parent company, Triton Agency, LLC, not by a third party. We don't use a payment processor yet.
Bug bounty & vulnerability disclosure
Found something? We want to hear about it.
How to report
Email security@teamsmfa.com with what we need to reproduce it: the URL or endpoint, the steps, and what you were able to access. A proof of concept helps. Our machine-readable contact details are in /.well-known/security.txt.
What we promise
- We'll acknowledge your report within 2 business days.
- Within 10 business days, we'll share our assessment and whether we're fixing it.
- We'll keep you updated until it's resolved, and credit you publicly if you'd like.
Rewards
We don't offer cash rewards yet, and we'd rather say so than advertise a bounty we haven't budgeted for. Valid reports earn public credit on this page (with your permission) and our sincere thanks. Reports we value most:
- Retrieving a TOTP seed, or a code from a team you're not a member of
- Bypassing the passkey step or taking over another account
- Keeping access after being removed from a team
- Reading or changing data across teams or tenants
Safe harbor
If you make a good-faith effort to follow this policy while researching, we won't pursue or support legal action against you. We'll treat your research as authorized under the Computer Fraud and Abuse Act and similar laws. If a third party takes action against you over research that followed this policy, we'll make clear that it was authorized.
Scope and rules
- In scope: teamsmfa.com, its subdomains, and the TeamsMFA API.
- Out of scope: Cloudflare's or other vendors' infrastructure (report those to the vendor); automated scanner output with no demonstrated impact; missing best-practice headers with no exploit; rate-limit or volume findings that need denial of service.
- Please do use teams and accounts you created yourself, and stop as soon as you've confirmed access.
- Please don't access or change other users' data, degrade the service, or social-engineer our staff or customers.
- Give us time. Let us fix the issue before you publish, and we'll agree on a timeline with you.
Still have questions?
Security questions go to a person, not a ticket queue. Ask about anything on this page, and you'll get a specific answer, even if it's "we don't do that yet."