Two-Factor Authentication with TOTP in SoliLang
Two-factor authentication (2FA) adds an extra layer of security to your accounts. Instead of just knowing something (password), the user must also have something (their phone). TOTP (Time-based One-Time Password) is the standard used by Google Authenticator, Authy, and most authenticator apps.
What is TOTP?
TOTP generates a 6-digit code that changes every 30 seconds. It's based on:
- A shared secret key (Base32 encoded)
- The current timestamp
- A cryptographic hash (HMAC-SHA1)
The same code is generated on the server and the authenticator app, so no network connection is needed.
Generating TOTP Codes
Let's start with generating a TOTP code:
secret = "JBSWY3DPEHPK3PXP"
code = Crypto.totp_generate(secret)
println(code) # e.g., "123456"
The totp_generate function takes:
secret(required) - Your Base32-encoded shared secrettime(optional) - Unix timestamp (defaults to current time)period(optional) - Code validity window in seconds (defaults to 30)
Verifying TOTP Codes
When a user enters their 2FA code, verify it:
secret = "JBSWY3DPEHPK3PXP"
user_code = request.body["code"]
if Crypto.totp_verify(secret, user_code)
# Code is valid - user is authenticated
println("2FA successful!")
else
# Invalid code
println("Invalid code, try again")
end
The verify function accepts previous, current, and next time windows to handle clock drift between server and phone.
Creating QR Codes for Easy Setup
The easiest way for users to add 2FA is by scanning a QR code. Generate the URI first:
secret = "JBSWY3DPEHPK3PXP"
uri = Crypto.totp_uri(secret, "[email protected]", "MyApp", 30)
println(uri)
# otpauth://totp/MyApp:user%40example.com?secret=JBSWY3DPEHPK3PXP&issuer=MyApp&algorithm=SHA1&digits=6&period=30
Then encode it as a QR code:
qr_data = QRCode.encode(uri)
Display qr_data to the user as an image.
Complete Example: Adding 2FA to Login
Here's a full example of adding TOTP-based 2FA to a login flow:
# app/controllers/auth_controller.sl
fn login
params = req["all"]
email = params["email"]
password = params["password"]
user = User.find_by_email(email)
if user == nil or not Crypto.argon2_verify(password, user["password_hash"])
return render("auth/login", {"error": "Invalid credentials"})
end
if user["totp_secret"] != nil
# User has 2FA enabled - require code
temp_token = generate_temp_token(user["id"])
return render("auth/verify_2fa", {"temp_token": temp_token})
end
# No 2FA - complete login
session["user_id"] = user["id"]
redirect("/dashboard")
end
fn verify_2fa
params = req["all"]
temp_token = params["temp_token"]
code = params["code"]
user_id = validate_temp_token(temp_token)
if user_id == nil
return redirect("/login")
end
user = User.find(user_id)
if not Crypto.totp_verify(user["totp_secret"], code)
return render("auth/verify_2fa", {
"temp_token": temp_token,
"error": "Invalid code"
})
end
# 2FA verified - complete login
session["user_id"] = user["id"]
redirect("/dashboard")
end
Enabling 2FA for a User
To help users set up 2FA, generate their secret and QR code:
# app/controllers/settings_controller.sl
fn enable_2fa
user = User.find(session["user_id"])
# Generate a new random secret
keypair = Crypto.x25519_keypair
secret = base64_encode(keypair["private"][0..20]) # 20 bytes = 32 Base32 chars
# Save to user (in production, encrypt this!)
user["totp_secret"] = secret
user.save
# Generate QR code URI
uri = Crypto.totp_uri(secret, user["email"], "MyApp", 30)
qr_code = QRCode.encode(uri)
render("settings/2fa_setup", {"qr_code": qr_code, "secret": secret})
end
Security Best Practices
-
Encrypt secrets at rest - TOTP secrets are sensitive. Encrypt them in your database.
-
Rate limit verification - Prevent brute-force attacks by limiting verification attempts.
-
Allow backup codes - Users may lose their phone. Provide backup codes as an alternative.
-
Use HTTPS - Always transmit codes over encrypted connections.
Conclusion
TOTP is a battle-tested 2FA standard that's easy to implement in SoliLang. With just three functions:
Crypto.totp_generate()- Create codesCrypto.totp_verify()- Validate codesCrypto.totp_uri()- Generate QR code URIs
You can add professional-grade two-factor authentication to your application in minutes.