Skip to content
Report library
Purpose / Development

Two Factor Authentication Best Practices Skill Security Audit

What the author says it does (original text)

Configure TOTP authenticator apps, send OTP codes via email/SMS, manage backup codes, handle trusted devices, and implement 2FA sign-in flows using Better Auth's twoFactor plugin. Use when users need MFA, multi-factor authentication, authenticator setup, or login security with Better Auth.

Independent security check

Security risks found

Files checked
1
Risks found
4
Could it run dangerous commands?Looks for programs run straight after downloading, remote control of your computer, and hidden commands.Risks found: 1
Medium risk

Setup executes the registry's current latest CLI and migrates the authentication schema

Source references: 1
What we found

The guide instructs users to run `npx auth@latest migrate`. The `@latest` tag does not pin a specific reviewed version, so executed behavior can change as the tag moves; the migration also makes lasting changes to the authentication database schema.

Why this matters

If the newest upstream release is compromised, changes behavior, or is incompatible with the project, the command could run unexpected code with developer permissions or cause authentication-table changes, deployment failures, and login outages.

The guide lists `npx auth@latest migrate` as a setup step. It obtains whichever CLI is tagged latest at that time, and the migration step is intended to change the built-in adapter's data structure; the same line directs Drizzle/Prisma users to generate and push changes. Exact impact depends on the project and tool version, so users should confirm the version, generated migration, backup, and rollback plan first.

SKILL.md:8In the instructionsOpen original file
1. Add `twoFactor()` plugin to server config with `issuer`2. Add `twoFactorClient()` plugin to client config3. Run `npx auth@latest migrate` (built-in adapter) or generate + push for Drizzle/Prisma4. Verify: check that `twoFactorSecret` column exists on user table
Could it expose your files or keys?Looks for uploads of files containing passwords or keys, and keys written directly in the code.Risks found: 1
Medium risk

The configuration interface explicitly permits plaintext OTP and backup-code storage

Source references: 3
What we found

The guide lists `storeOTP: "plain"` and `storeBackupCodes: "plain"` as valid configurations. Although the complete example uses encryption, copying or selecting a plaintext option leaves account-verification or recovery secrets readable in the database.

Why this matters

Database read access, leaked backups, accidental logging, or SQL injection could expose unexpired OTPs or unused backup codes and weaken second-factor protection.

The documentation explicitly lists `plain` as an available storage mode for OTP and backup codes. If selected, a database disclosure or a person with database read access could directly obtain still-valid verification or recovery codes. However, the complete configuration uses `encrypted`, and the text says backup codes are encrypted by default, so plaintext is not the demonstrated default. Users can restrict storage to encrypted or hashed modes and ask why plaintext is exposed.

SKILL.md:137In the instructionsOpen original file
```tstwoFactor({  otpOptions: {    storeOTP: "encrypted", // Options: "plain", "encrypted", "hashed"  },});```
Show 2 other places
SKILL.md:205In the instructionsOpen original file
```tstwoFactor({  backupCodeOptions: {    amount: 10, // Number of codes to generate (default: 10)    length: 10, // Length of each code (default: 10)    storeBackupCodes: "encrypted", // Options: "plain", "encrypted"  },});```
SKILL.md:273In the instructionsOpen original file
TOTP secrets: encrypted with auth secret. Backup codes: encrypted by default. OTP: configurable (`"plain"`, `"encrypted"`, `"hashed"`). Uses constant-time comparison for verification.
Could it delete files or keep running?Looks for broad file deletion, disk overwrites, and programs set to start automatically.No risks found
Could it bypass safety checks?Looks for skipped website security checks, excessive file access, or actions that skip your approval.Risks found: 2
Medium risk

Every verification example trusts the device for 30 days by default

Source references: 4
What we found

The TOTP, OTP, and backup-code examples all hard-code `trustDevice: true`. The guide says trust lasts 30 days by default and refreshes on each sign-in, so copying the examples creates a long-lived second-factor exemption without an explicit user choice.

Why this matters

On a shared, lost, or session-compromised device, someone with the trusted state may be able to sign in using only the primary credential for an extended period. Continued sign-ins may keep extending the exemption. Backup-code recovery also automatically trusts the device.

The TOTP, OTP, and backup-code examples all enable `trustDevice: true`, while the text says trust lasts 30 days by default and refreshes at each sign-in. Copying them creates a potentially continuing second-factor bypass without showing user consent or revocation controls. A user can ask that it default to false and only be enabled by an explicit choice.

SKILL.md:80In the instructionsOpen original file
```tsconst verifyTotp = async (code: string) => {  const { data, error } = await authClient.twoFactor.verifyTotp({    code,    trustDevice: true,  });};
Show 3 other places
SKILL.md:130In the instructionsOpen original file
Send: `authClient.twoFactor.sendOtp()`. Verify: `authClient.twoFactor.verifyOtp({ code, trustDevice: true })`.
SKILL.md:194In the instructionsOpen original file
```tsconst verifyBackupCode = async (code: string) => {  const { data, error } = await authClient.twoFactor.verifyBackupCode({    code,    trustDevice: true,  });};
SKILL.md:245In the instructionsOpen original file
Pass `trustDevice: true` when verifying. Default trust duration: 30 days (`trustDeviceMaxAge`). Refreshes on each sign-in.
Low risk

The install command does not pin a dependency version

Source references: 1
What we found

The installation command does not specify dependency versions. The same command may download different code later, so what you install can differ from what was checked.

Why this matters

A later install may download different code even though the command and this report have not changed.

The setup explicitly uses `auth@latest` rather than a fixed version. The registry's `latest` tag may resolve to different code over time, so this material does not identify the exact version that will run. A user can ask the author to pin and document a tested CLI version.

SKILL.md:10In the instructionsOpen original file
2. Add `twoFactorClient()` plugin to client config3. Run `npx auth@latest migrate` (built-in adapter) or generate + push for Drizzle/Prisma4. Verify: check that `twoFactorSecret` column exists on user table
Could it mislead the AI or hide text?Checks the skill instructions for requests to ignore you, influence the report, or hide text in invisible characters.No risks found
Could it change links or payment recipients without asking?Looks for forced referral or payment changes combined with instructions to hide the change.No risks found

Inside this skill

8 instruction sections

This Skill is a Better Auth configuration guide covering TOTP, email/SMS OTP, backup codes, trusted devices, and the sign-in verification flow. The supplied material contains instructions and examples, but no standalone scripts.

View source
SKILL.md:2In the instructionsOpen original file
---name: two-factor-authentication-best-practicesdescription: Configure TOTP authenticator apps, send OTP codes via email/SMS, manage backup codes, handle trusted devices, and implement 2FA sign-in flows using Better Auth's twoFactor plugin. Use when users need MFA, multi-factor authentication, authenticator setup, or login security with Better Auth.---

Enabling 2FA requires password verification, and 2FA is not marked enabled until the first TOTP verification succeeds, which avoids locking an account into an unfinished setup.

View source
SKILL.md:46In the instructionsOpen original file
Requires password verification. Returns TOTP URI (for QR code) and backup codes.
SKILL.md:61In the instructionsOpen original file
`twoFactorEnabled` is not set to `true` until first TOTP verification succeeds. Override with `skipVerificationOnEnable: true` (not recommended).

The sign-in flow verifies primary credentials, redirects to a second-factor page, and creates the session cookie only after successful second-factor verification.

View source
SKILL.md:220In the instructionsOpen original file
1. Call `signIn.email({ email, password })`2. Check `context.data.twoFactorRedirect` in `onSuccess`3. If `true`, redirect to `/2fa` verification page4. Verify via TOTP, OTP, or backup code5. Session cookie is created on successful verification

The example uses the application's existing email function to send the OTP to the current user's email address; it does not show delivery to a third-party address.

View source
SKILL.md:112In the instructionsOpen original file
      otpOptions: {        sendOTP: async ({ user, otp }, ctx) => {          await sendEmail({            to: user.email,            subject: "Your verification code",            text: `Your code is: ${otp}`,          });        },
Start here · InstructionsSKILL.md
two-factor-authentication-best-practices
Lines connect the instruction file to its sections, not an observed execution order. Select a section to read the source. 2 more sections are available in the original file.
Files and check records1 files

Coverage and gaps

Content covered in each file

These are the source ranges included in this check, not a guarantee that every issue has been resolved.

  • SKILL.mdFull text included

This report is for the version above. We read the available code and instructions without running the skill or checking extra packages it installs. This is not a promise of safety: a different version or setup may behave differently.

  • SKILL.mdInstructions

Operations mentioned in code and instructions

Install extra software packages
SKILL.md:10In the instructionsOpen original file
2. Add `twoFactorClient()` plugin to client config3. Run `npx auth@latest migrate` (built-in adapter) or generate + push for Drizzle/Prisma4. Verify: check that `twoFactorSecret` column exists on user table
Read keys or account settings
SKILL.md:251In the instructionsOpen original file
Flow: credentials → session removed → temporary 2FA cookie (10 min default) → verify → session created.
Lines read
332
File checksum (to compare versions)
44b63656861a855682212954b64126479d63be9e7ca87db748bd84dee8a341c3