Skip to main content
Add MFA with TOTP authenticator apps.

Add MFA device

Register a new TOTP authenticator device:
import com.dynamic.sdk.android.Models.MfaAddDevice

// Generate a new MFA secret
val deviceInfo: MfaAddDevice = sdk.mfa.addDevice("totp")

println("Secret: ${deviceInfo.secret}")
println("QR Code URI: otpauth://totp/YourApp:[email protected]?secret=${deviceInfo.secret}&issuer=YourApp")
The MfaAddDevice response contains:
  • secret: The TOTP secret to add to an authenticator app
  • Use this secret to generate a QR code for easy scanning

Generate QR code

Create a QR code for easy authenticator app setup:
import com.google.zxing.BarcodeFormat
import com.google.zxing.qrcode.QRCodeWriter
import android.graphics.Bitmap
import java.net.URLEncoder

fun generateQRCode(secret: String, accountName: String, issuer: String): Bitmap? {
    return try {
        val encodedIssuer = URLEncoder.encode(issuer, "UTF-8")
        val encodedAccount = URLEncoder.encode(accountName, "UTF-8")
        val totpUri = "otpauth://totp/$encodedIssuer:$encodedAccount?secret=$secret&issuer=$encodedIssuer"

        val writer = QRCodeWriter()
        val bitMatrix = writer.encode(totpUri, BarcodeFormat.QR_CODE, 512, 512)
        val width = bitMatrix.width
        val height = bitMatrix.height
        val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)

        for (x in 0 until width) {
            for (y in 0 until height) {
                bitmap.setPixel(
                    x, y, 
                    if (bitMatrix[x, y]) android.graphics.Color.BLACK 
                    else android.graphics.Color.WHITE
                )
            }
        }
        bitmap
    } catch (e: Exception) {
        null
    }
}

// Usage
val user = sdk.auth.authenticatedUser
val accountName = user?.email ?: user?.userId ?: "Account"
val issuer = "YourApp"
val qrBitmap = generateQRCode(deviceInfo.secret, accountName, issuer)

Verify MFA device

After adding the secret to an authenticator app, verify it with a code:
// User enters the 6-digit code from their authenticator app
val verificationCode = "123456"

sdk.mfa.verifyDevice(verificationCode, "totp")

Get MFA devices

List all registered MFA devices:
import com.dynamic.sdk.android.Models.MfaDevice

val devices: List<MfaDevice> = sdk.mfa.getUserDevices()

devices.forEach { device ->
    println("Device ID: ${device.id}")
    println("Type: ${device.type}")
    println("Created: ${device.createdAt}")
}

Remove MFA device

Delete an MFA device:
val deviceId = "device-id"
val mfaAuthToken = "mfa-auth-token" // Obtained from MFA authentication
sdk.mfa.deleteUserDevice(deviceId, mfaAuthToken)

Get recovery codes

Retrieve MFA recovery codes:
val recoveryCodes: List<String> = sdk.mfa.getRecoveryCodes()

recoveryCodes.forEach { code ->
    println("Recovery code: $code")
}

Complete MFA setup flow

Here’s a complete example of the MFA setup flow:
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.Models.MfaAddDevice
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class MfaSetupViewModel(private val sdk: DynamicSDK) : ViewModel() {
    private val _deviceInfo = MutableStateFlow<MfaAddDevice?>(null)
    val deviceInfo: StateFlow<MfaAddDevice?> = _deviceInfo

    private val _verificationCode = MutableStateFlow("")
    val verificationCode: StateFlow<String> = _verificationCode

    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading

    private val _error = MutableStateFlow<String?>(null)
    val error: StateFlow<String?> = _error

    fun generateSecret() {
        viewModelScope.launch {
            _isLoading.value = true
            _error.value = null
            try {
                val device = sdk.mfa.addDevice("totp")
                _deviceInfo.value = device
            } catch (e: Exception) {
                _error.value = "Failed to generate secret: ${e.message}"
            }
            _isLoading.value = false
        }
    }

    fun updateCode(code: String) {
        _verificationCode.value = code
    }

    fun verifyDevice(onSuccess: () -> Unit) {
        viewModelScope.launch {
            _isLoading.value = true
            _error.value = null
            try {
                sdk.mfa.verifyDevice(_verificationCode.value.trim(), "totp")
                onSuccess()
            } catch (e: Exception) {
                _error.value = "Verification failed: ${e.message}"
            }
            _isLoading.value = false
        }
    }
}

Supported authenticator apps

The Dynamic SDK works with any TOTP-compatible authenticator app:
  • Google Authenticator
  • Microsoft Authenticator
  • Authy
  • 1Password
  • Bitwarden
  • LastPass Authenticator