Skip to content

Home / Glossary / MFA (Multi-Factor Authentication)

Tech & IT

MFA (Multi-Factor Authentication)

By Sitraka Forler · Lecturer, Durham Business SchoolUpdated 3 October 2026 About this site

Proving who you are with two or more different kinds of evidence, not just a password.

Also known as: 2FA, two-factor authentication.

Factors must come from different categories: something you know (a password), have (a phone or security key) or are (a fingerprint), so a password plus a security question is not MFA. A leaked password alone then unlocks nothing; two-factor authentication (2FA) is MFA with exactly two. Text-message (SMS) codes can be hijacked, authenticator apps are safer, and passkeys and security keys resist phishing.

The intuition

A password is a secret that works for whoever holds it. Once it has been typed into a fake page, leaked from another site's database or simply guessed, the server has no way to tell the thief from the owner. Multi-factor authentication makes a login demand evidence of two or more kinds, chosen so that the attack which steals one kind does nothing to the other. Passwords are stolen remotely and often in bulk; a phone or a security key normally has to be taken from somebody's pocket; a fingerprint is usually checked on a device the person already holds. That independence is the whole idea. NIST SP 800-63B, the US federal guideline on digital authentication, applies the same logic to biometrics: noting that a face or a fingerprint is not a secret, it does not recognise a biometric as an authenticator by itself and allows one only alongside a physical authenticator. The usual pairing is a password plus one possession factor, so the interesting question is how that possession gets proven.

Three methods are in everyday use. A text message is the simplest: the server picks a random code and sends it to your number, so what you prove is control of a phone number. An authenticator app needs no message. At enrolment the site shows a QR code carrying a random secret, and from then on the app and the server each hold a copy. Both compute the counter T = floor(Unix time / 30), run it with the secret through HMAC and cut the result to six digits, the TOTP method of RFC 6238, so a matching code proves the app holds the secret without the secret being sent at login. FIDO2 security keys and passkeys use public-key cryptography instead. The device creates a key pair for each site, the site stores only the public half, and each login signs a fresh challenge. The browser lets a page use only keys registered for the domain it is really on, so a lookalike domain cannot obtain a signature the real site will accept.

Each method fails in its own place. A phone number can be moved to an attacker's SIM through the carrier, and SP 800-63B, which asks verifiers to watch for SIM changes and number porting, lists delivery over the public telephone network, SMS or voice, as its only restricted authenticator: allowed, but only with a risk assessment, notice to users, an unrestricted alternative and a migration plan. Anything you type can be phished. A fake login page can pass your password and your six-digit code to the real site while the code is still valid, and the guideline states that OTP and out-of-band codes are not phishing-resistant, because typing a code does not bind it to the session. Practitioners therefore offer a phishing-resistant option alongside codes, which the current revision requires at its middle level, AAL2; cap consecutive failed attempts at no more than 100; accept each code only once; and audit account recovery, since a reset that falls back to SMS quietly undoes a hardware key. Synced passkeys, which NIST calls syncable authenticators, are exportable by design, so NIST accepts them up to AAL2 but not at AAL3.

Example

# pip install pyotp
import pyotp

# A time-based one-time password (TOTP) generator, like an authenticator app
totp = pyotp.TOTP(pyotp.random_base32())  # the random secret is what the app stores
print(totp.now())  # a 6-digit code that changes every 30 seconds

One six-digit code from the RFC 6238 test key, at a stated second and two seconds later

  1. Fix the parameters. Use the RFC 6238 defaults, start time T0 = 0 (the Unix epoch) and time step X = 30 seconds, with HMAC-SHA-1, the HOTP function of RFC 4226 on which TOTP is built, and a 6-digit code, the minimum RFC 4226 allows and the length authenticator apps usually show. The shared secret is the RFC's published test key, the 20 ASCII characters 12345678901234567890, so every value below can be checked against the test-vector table in RFC 6238. The stated moment is Unix time 1111111109, which is 18 March 2005 at 01:58:29 UTC.
  2. Compute the counter. T = floor((1111111109 - 0) / 30). Since 30 × 37037036 = 1111111080 and 1111111109 - 1111111080 = 29, the division gives 37037036 with remainder 29, so T = 37037036. That step began at Unix time 1111111080 (01:58:00 UTC) and its last second is 1111111080 + 29 = 1111111109, so the code on screen has one second left to live.
  3. Encode the counter as 8 bytes, big-endian. In hexadecimal 37037036 is 23523EC. Check: 2 × 16⁶ + 3 × 16⁵ + 5 × 16⁴ + 2 × 16³ + 3 × 16² + 14 × 16 + 12 = 33554432 + 3145728 + 327680 + 8192 + 768 + 224 + 12 = 37037036. Padded to 8 bytes the message is 00 00 00 00 02 35 23 EC, which is the RFC's Value of T column for this timestamp.
  4. Hash and truncate. HMAC-SHA-1 of those 8 bytes under the key gives 20 bytes: 278c02e53610f84c40bd9135acd4101012410a14. Dynamic truncation, defined in RFC 4226, reads the low 4 bits of the last byte, 0x14, which are 4, and takes the 4 bytes starting at offset 4 (counting the first byte as offset 0): 36 10 F8 4C. As a number that is 54 × 16777216 + 16 × 65536 + 248 × 256 + 76 = 905969664 + 1048576 + 63488 + 76 = 907081804. The top bit is already 0, because 0x36 is below 0x80, and 907081804 mod 1000000 = 81804, which padded to six digits gives the code 081804. The RFC lists the eight-digit form 07081804, which ends in the same six digits.
  5. Move two seconds on, to 1111111111 (01:58:31 UTC). Now 1111111111 - 1111111080 = 31, which is at least 30, so T = 37037036 + 1 = 37037037 with remainder 31 - 30 = 1: a new step, 23523ED in hexadecimal. The new HMAC is b0092b21d048af209da0a1ddd498ade8a79487ed; its last byte 0xED has low bits 1101, which is 13, so the 4 bytes at offset 13 are 98 AD E8 A7 = 152 × 16777216 + 173 × 65536 + 232 × 256 + 167 = 2550136832 + 11337728 + 59392 + 167 = 2561534119. This time the top bit is set, so clearing it subtracts 2³¹ = 2147483648: 2561534119 - 2147483648 = 414050471, and 414050471 mod 1000000 = 50471 gives the code 050471 (the RFC's eight-digit value is 14050471).
  6. Let the verifier absorb the delay. A user who reads 081804 at 01:58:29 and submits it at 01:58:31 arrives in step 37037037, where the current code is 050471, so a verifier that checks only the current step rejects a genuine user. RFC 6238 recommends allowing at most one time step for network delay; with that allowance the verifier also computes step 37037036, matches 081804, and records it so the same code cannot be accepted a second time. The cost is a longer life: the code for step 37037036 is now accepted from 1111111080 to 1111111139, which is 1111111139 - 1111111080 + 1 = 60 seconds.
  7. Price a blind guess. Six digits allow 10⁶ = 1,000,000 codes, and with two steps accepted a single guess can match at most 2 of them, a chance of 2 / 1,000,000 = 1 in 500,000. SP 800-63B requires the verifier to disable the authenticator after no more than 100 consecutive failed attempts, so a run of blind guesses that ends in that lockout succeeds with a chance of at most 100 × 2 / 1,000,000 = 200 / 1,000,000 = 0.02%, or 1 in 5,000. The count is of consecutive failures, and the guideline says a successful login should clear it, so the bound applies to each run of guesses, not to the life of the account. Without the cap the same arithmetic runs away: 10,000 guesses would reach up to 10,000 × 2 / 1,000,000 = 2%, so the limit on attempts matters as much as the length of the code.

An authenticator code is only floor(Unix time / 30) run through HMAC with a shared secret and cut to six digits, so its strength rests on the secret staying secret, on a verifier that allows no more than one step of delay and accepts each code once, and on a hard cap on guesses.

Common pitfalls

  • Calling a second step a second factor. An emailed code is only as strong as the mailbox, and if that mailbox opens with the same reused password, one leak unlocks both. SP 800-63B goes further and bars email from out-of-band authentication altogether, although it still allows recovery codes to be sent by email. Before counting a factor, check that stealing it would need a different attack from stealing the first.
  • Leaving a weak fallback switched on. Enrolling a security key changes nothing if the login page still offers to send a text instead, or if account recovery resets everything through SMS: an attacker simply takes the weakest path. Judge an account by every method it accepts, recovery included, and remove the ones you would not choose on purpose.
  • Believing an authenticator app stops phishing. It removes SIM swaps and intercepted texts, but a six-digit code typed into a fake page can be relayed to the real site while it is still valid, and SP 800-63B states plainly that OTP authentication is not phishing-resistant. Only cryptographic authentication bound to the site's name or to the connection, such as FIDO2 security keys and passkeys, meets its definition.
  • Widening the time window to hide clock problems. Accepting several steps either side of now keeps each code alive for minutes and multiplies the odds of a blind guess: at two steps either side a guess can match up to 2 + 1 + 2 = 5 codes instead of 2. Keep the delay allowance to one step, as RFC 6238 recommends; handle drift as its resynchronisation section describes, by recording each device's offset in steps after a successful login rather than widening the window for everyone; refuse a code that has already been used; and enforce the limit on failed attempts.
  • Treating the TOTP secret as harmless. The server stores the same symmetric secret the app holds, so a leaked table of seeds lets an attacker generate every user's codes, and the enrolment QR code carries the secret unencrypted, so a screenshot of it in a shared folder is a working copy of the factor. Encrypt the seeds at rest, as RFC 6238 recommends, restrict them to the service that verifies codes, and never log or email the QR image.

Frequently asked questions

What is MFA (multi-factor authentication)?

MFA is a login that asks for evidence from at least two different categories: something you know, something you have and something you are. The categories are chosen because the attacks on them differ: a password can be phished or leaked from a database, while a phone or security key normally has to be physically taken. A password followed by a code from an authenticator app is MFA, and so is a passkey unlocked with your fingerprint, which combines possession of the device with a biometric check on it.

What is the difference between 2FA and MFA?

The two terms overlap almost completely. MFA means two or more factor types, and 2FA names the case where there are exactly two, the everyday case: a password followed by a code or a key. Every 2FA login is therefore MFA, while a login that adds a third factor, such as a smart card, its PIN and a fingerprint, is MFA without being 2FA. The categories matter more than the count: a password and a PIN are both things you know, so together they still supply only one kind of factor.

How do authenticator app codes work?

At setup the site shows a QR code carrying a random secret, and the app and the server each keep a copy. Both then compute the counter floor(Unix time / 30), run it with the secret through HMAC, usually HMAC-SHA-1, and cut the result to six digits, the TOTP method set out in RFC 6238. If your code matches the one the server computes, you have shown that you hold the secret without sending it. At Unix time 1111111109 the counter is 37037036, and the RFC's test key turns it into the code 081804.

Why are SMS codes less secure than an authenticator app?

A text message proves control of a phone number, not of a particular device. The number can be moved to an attacker's SIM card through the carrier or ported to another network, and a phone that shows texts on its lock screen hands the code to anyone holding it. NIST SP 800-63B lists SMS and voice delivery as its only restricted authenticator, usable only after a risk assessment and with alternatives offered to every user. An authenticator app computes its codes without the phone network, although a code from either one can still be phished by a fake login page.

What is a passkey and why is it phishing-resistant?

A passkey is a FIDO2 credential: a key pair your device creates for one site, where the site stores only the public key and the private key stays on the device or is copied between your devices in encrypted form by a sync service. At login the device signs a fresh challenge once you unlock it with a fingerprint, face or PIN. The browser lets a page use only keys registered for the domain it is actually on, so a lookalike domain cannot get a signature the real site will accept, and there is no code for you to type into the wrong place.

Do authenticator apps need an internet connection?

No. The code is computed on the phone from the stored secret and the phone's clock, and nothing leaves the device until you type the code in. What the app does need is the right time: if the phone's clock drifts further than the one step or so the server tolerates, every code is rejected. Switching on automatic date and time on the phone usually fixes it.

Test yourself

Learn by doing - free, in your browser

Related terms