Careeroza
Real World Example
Where You See This
- Flipkart, Swiggy, Zepto: login with mobile number + SMS OTP — a custom provider exactly like ours.
- Zerodha Kite, Groww: password + TOTP (or app-based PIN) as required by SEBI two-factor rules.
- Gmail, GitHub, AWS console: password + TOTP or passkeys, and remember-me "Trust this device for 30 days".
ShopEase Logins
| User | Login method | Second factor | Session |
|---|---|---|---|
| Customer (app) | Mobile OTP (custom provider) or Google | none for browsing; OTP again for changing phone/email | 15-min JWT + rotating refresh token |
| Seller | Email + password (form login) | TOTP mandatory | session + remember-me 14 days; payouts need full login |
| Admin | Email + password | TOTP mandatory, office VPN only | session, 30-min idle timeout, no remember-me |
// Seller enrols in TOTP: generate a secret and show it as a QR code
@PostMapping("/api/seller/mfa/setup")
public MfaSetupResponse setup(@AuthenticationPrincipal UserDetails seller) {
byte[] secret = new byte[20];
secureRandom.nextBytes(secret);
String base32 = Base32.encode(secret); // authenticator apps use Base32
sellers.savePendingMfaSecret(seller.getUsername(), encryptor.encrypt(base32)); // encrypted at rest
String otpauth = "otpauth://totp/ShopEase:" + seller.getUsername()
+ "?secret=" + base32 + "&issuer=ShopEase&digits=6&period=30";
return new MfaSetupResponse(otpauth); // frontend renders this as a QR code
}Note: The MFA secret is like a password the server must be able to read back, so it is encrypted (not hashed) at rest. Only after the seller types one valid code is the pending secret activated. Also give 10 one-time recovery codes (stored hashed) for a lost phone.