Two-Factor¶
The two-factor plugin adds TOTP, OTP, and backup-code flows on top of an existing primary sign-in method such as credential-password or OAuth.
Install¶
go get github.com/ragokan/limen/plugins/two-factor
Enable¶
import twofactor "github.com/ragokan/limen/plugins/two-factor"
auth, err := limen.New(&limen.Config{
Database: adapter,
Secret: []byte(os.Getenv("LIMEN_SECRET")),
Plugins: []limen.Plugin{
credentialpassword.New(),
twofactor.New(),
},
})
The plugin uses Config.Secret by default. Use twofactor.WithSecret when you
need a separate secret.
Routes¶
With WithHTTPBasePath("/api/auth"), routes are mounted under
/api/auth/two-factor.
POST /api/auth/two-factor/initiate-setup
GET /api/auth/two-factor/totp/uri
POST /api/auth/two-factor/finalize-setup
POST /api/auth/two-factor/disable
POST /api/auth/two-factor/verify
POST /api/auth/two-factor/otp/send
GET /api/auth/two-factor/backup-codes
PUT /api/auth/two-factor/backup-codes
Setup, disable, TOTP URI, and backup-code routes require an authenticated session. Login verification uses the challenge cookie created after primary sign-in.
Login Flow¶
When a user with two-factor enabled signs in successfully, the plugin removes the session response and returns:
{
"two_factor_required": true
}
The client then submits the second factor:
POST /api/auth/two-factor/verify
Content-Type: application/json
{
"code": "123456",
"method": "totp"
}
On success, Limen creates the final session.
Session Rotation¶
By default, enabling, disabling, and verifying two-factor rotates sessions and revokes other sessions on state changes. Keep that default for production account security unless you have a specific compatibility reason to change it.