Adding Two‑Factor Authentication (2FA) to a Laravel API (Laragear + Sanctum)
I recently wired up Two‑Factor Authentication (2FA) for the Profresh API. There are a few ways to do 2FA in Laravel, but I chose the Laragear 2FA package because it’s simple, well‑documented, and plays nicely with Sanctum.
2 min read


We’ll add 2FA to a Laravel Sanctum API in 3 steps
Enable 2FA (generate secret + QR, confirm with OTP)
Return recovery codes & allow disabling
Enforce 2FA at login with a secure flow
I used the Laragear 2FA package (v2 for Laravel 10) with Sanctum. Full code is in my repo: ProFresh GitHub.
Who this is for
You’re building a JSON API with Laravel 10 + Sanctum.
You’re comfortable with basic auth flows and Form Requests.
You want a clean, minimal 2FA setup without writing your own logic
Step 1: Enable 2FA
When a user opts-in, we generate a secret, return a QR code, and mark the setup as “in progress.”
What happens:
Creates a pending secret
Returns QR (base64) URI and raw string,
Mark status as in_progress. -> See related form request file
Step 2: Confirm & Recovery Codes
The user confirms setup with a 6-digit code from their authenticator app.
What happens:
Validate the OTP
Enable 2FA
Return one time recovery codes (treat like password) -> See related form request file
Step 3: Regenerate Codes or Disable 2FA (optional)
Disable: Gives user option to disable 2FA
Recovery codes endpoint: - Generates a fresh set of recovery codes on demand
Step 4: Enforce 2FA at login
Here’s the secure API login flow:
User submits email/password.
If 2FA is disabled return a Sanctum token like normal.
If 2FA is enabled create a short‑lived, encrypted session marker and return 2fa required.
User submits the 6‑digit code to a dedicated endpoint.
If the code is valid clear the marker and issue the Sanctum token.
Controller: confirm login
What happens here:
If 2FA is enabled, no token is returned yet — only a two_fa_required response.
If 2FA is not enabled, we proceed as normal and issue the token.
Set session in LoginUserService class
Confirming the login:
Once the user enters their OTP, the second endpoint kicks in:
What this does:
Validates the 6-digit code via the TwoFactorLoginRequest.
If valid, it clears the short-lived 2fa_login marker from the session.
Finally, it issues a first-class Sanctum token, just like a normal login.
🏁 Wrapping Up
And that’s it — you now have a full 2FA setup running in your Laravel API.
This approach keeps your login flow clean, secure, and simple enough to extend later.
If you want to take it a step further, check out Securing articles on advanced MFA authentication — they cover some great extra security layers.
You can also look at the frontend side of this feature and explore the full implementation in my open-source project ProFresh.
If you hit any issues or have questions, feel free to reach out — I’ve probably run into the same thing.
Thanks for reading — hope this helps you make your next Laravel app a bit more secure!