TOTP Kotlin SDK
Integrate secure Time-based One-Time Password (TOTP) capabilities into your Android applications. Manage encrypted tenant enrollments, customizable validation algorithms, and hardware-backed KeyStore operations.
The Fyno TOTP (Time-based One-Time Password) SDK is an Android library that provides secure TOTP generation and management capabilities. It handles tenant enrollment, secret key encryption, TOTP generation with configurable algorithms and parameters, and secure key revocation.
Package Name: io.fyno.kotlin-sdk.totp
Min SDK: 23
Target SDK: 33+
Language: Kotlin
Table of Contents
- Installation
- Core Components
- API Reference
- Usage Examples
- Data Models
- Error Handling
- Security Considerations
Installation
Gradle Dependency
Add to your build.gradle file:
dependencies {
implementation 'io.fyno.kotlin-sdk:totp:1.0.0'
}Minimum Requirements
- API Level: 23+
- Android SDK: API 33+ for compilation
- Java/Kotlin: Java 8+
Core Components
FynoTOTP Class
The main entry point for all TOTP operations. Handles initialization, tenant management, and OTP generation.
Constructor:
FynoTOTP(context: Context)API Reference
init()
Initializes the SDK with workspace and user identifiers.
Signature:
fun init(
wsid: String,
distinctId: String,
callback: (Result<Unit>) -> Unit
): UnitParameters:
| Parameter | Type | Description |
|---|---|---|
wsid | String | Workspace ID for the application |
distinctId | String | Unique identifier for the user/device |
callback | (Result) -> Unit | Callback executed on Main thread with initialization result |
Returns: Unit (asynchronous via callback)
Throws: Exception details passed via Result.failure()
Example:
val fynoTotp = FynoTOTP(context)
fynoTotp.init("workspace_123", "user_456") { result ->
result.onSuccess {
Log.d("TOTP", "Initialization successful")
}
result.onFailure { error ->
Log.e("TOTP", "Initialization failed: ${error.message}")
}
}registerTenant()
Registers a tenant and stores their TOTP secret securely.
Signature:
fun registerTenant(
tenantId: String,
tenantLabel: String,
totpToken: String,
callback: (Result<Unit>) -> Unit
): UnitParameters:
| Parameter | Type | Description |
|---|---|---|
tenantId | String | Unique identifier for the tenant |
tenantLabel | String | Human-readable label/name for the tenant |
totpToken | String | TOTP secret key received from Fyno |
callback | (Result) -> Unit | Callback executed on Main thread with registration result |
Returns: Unit (asynchronous via callback)
Throws: Exception details passed via Result.failure()
Notes:
- The TOTP secret is encrypted using Android KeyStore before storage
- The tenant is automatically marked as ACTIVE upon successful registration
- This operation requires API 23+ due to KeyStore requirements
Example:
fynoTotp.registerTenant(
tenantId = "tenant_001",
tenantLabel = "My App Account",
totpToken = "JBSWY3DPEBLW64TMMQQ======",
callback = { result ->
result.onSuccess {
Toast.makeText(context, "Tenant registered", Toast.LENGTH_SHORT).show()
}
result.onFailure { error ->
Toast.makeText(context, "Registration failed: ${error.message}", Toast.LENGTH_SHORT).
setConfig()
Sets TOTP configuration parameters for a registered tenant.
Signature:
fun setConfig(
tenantId: String,
config: TotpConfig,
callback: (Result<Unit>) -> Unit
): UnitParameters:
| Parameter | Type | Description |
|---|---|---|
tenantId | String | Unique identifier for the tenant |
config | TotpConfig | Configuration object with TOTP parameters |
callback | (Result) -> Unit | Callback executed on Main thread with result |
Returns: Unit (asynchronous via callback)
Throws: Exception details passed via Result.failure()
Notes:
- Tenant must be registered before setting config
- Common algorithms:
SHA1,SHA256,SHA512 - Default values: digits=6, period=30, algorithm=SHA1
Example:
val config = TotpConfig(
tenant_name = "My App Account",
digits = 6,
algorithm = "SHA1",
period = 30
)
fynoTotp.setConfig(
tenantId = "tenant_001",
config = config,
callback = { result ->
result.onSuccess {
Log.
getTotp()
Generates and retrieves the current TOTP code for a tenant.
Signature:
@RequiresApi(Build.VERSION_CODES.M)
fun getTotp(
tenantId: String,
callback: (Result<Unit>) -> Unit
)Parameters:
| Parameter | Type | Description |
|---|---|---|
tenantId | String | Unique identifier for the tenant |
callback | (Result) -> Unit | Callback executed on Main thread with OTP or null if inactive |
Returns: Unit (asynchronous via callback)
Returns in Callback:
String: TOTP codenull: If tenant is inactive or not found- Exception in
Result.failure()if generation fails
Notes:
- Requires API 23+ (uses KeyStore for decryption)
- Returns
nullif tenant status is INACTIVE - Current server time is used for generation (client-side)
- Generated code changes every 30 seconds (or configured period)
- Automatically decrypts the stored secret
Example:
fynoTotp.getTotp(
tenantId = "tenant_001",
callback = { result ->
result.onSuccess { otp ->
if (otp != null) {
etOtpInput.setText(otp)
Log.d("TOTP", "OTP: $otp")
} else {
Log.d("TOTP", "Tenant is inactive"
revokeTenant()
Revokes a tenant's TOTP enrollment and permanently deletes the stored secret.
Signature:
@RequiresApi(Build.VERSION_CODES.M)
fun revokeTenant(
tenantId: String,
callback: (Result<Unit>) -> Unit
): UnitFetches the active tenants.
func fetchActiveTenants(
completion: @escaping (Result<[ActiveTenant], Error>) -> Void
)Parameters:
| Parameter | Type | Description |
|---|---|---|
tenantId | String | Unique identifier for the tenant to revoke |
callback | (Result) -> Unit | Callback executed on Main thread with revocation result |
Returns: Unit (asynchronous via callback)
Throws: Exception details passed via Result.failure()
Example:
fynoTotp.revokeTenant(
tenantId = "tenant_001",
callback = { result ->
result.onSuccess {
Log.d("TOTP", "Tenant revoked successfully")
// Update UI to remove tenant from list
}
result.onFailure { error ->
Log.e("TOTP", "Revocation failed: ${error.message}")
}
}
)Usage Examples
Complete Integration Flow
class TotpSetupActivity : AppCompatActivity() {
private lateinit var fynoTotp: FynoTOTP
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize SDK
fynoTotp = FynoTOTP(this)
fynoTotp.init("workspace_id", "user_id") { result ->
result.onSuccess {
Periodic OTP Refresh
class TotpRefreshService {
private lateinit var fynoTotp: FynoTOTP
private val handler = Handler(Looper.getMainLooper())
private val refreshInterval = 1000L // 1 second
fun startPeriodicRefresh(tenantId: String, onCodeUpdate: (String?) -> Unit) {
val runnable = object : Runnable {
Data Models
TotpConfig
Configuration model for TOTP generation parameters.
data class TotpConfig(
val tenant_name: String, // Human-readable tenant name
val digits: Int = 6, // Number of digits in OTP (typically 6-8)
val algorithm: String = "SHA1", // Hash algorithm (SHA1, SHA256, SHA512)
val period: Int = 30 // Time period in seconds (typically 30)
)Field Descriptions:
| Field | Type | Default | Description |
|---|---|---|---|
tenant_name | String | - | Display name for the tenant |
digits | Int | 6 | Length of generated OTP code |
algorithm | String | SHA1 | HMAC algorithm for OTP generation |
period | Int | 30 | Time window for OTP validity (seconds) |
Common Configurations:
// Standard Google Authenticator style
val standard = TotpConfig(
tenant_name = "Google Account",
digits = 6,
algorithm = "SHA1",
period = 30
)
// Enhanced security with SHA256
val enhanced = TotpConfig(
tenant_name = "Banking App",
digits = 8,
algorithm = "SHA256"
Error Handling
Exception Types
The SDK passes exceptions through Result.failure(). Common exceptions include:
| Exception | Cause | Handling |
|---|---|---|
InvalidKeyException | Encryption/decryption key error | Check KeyStore availability |
NoSuchAlgorithmException | Algorithm not supported | Verify algorithm parameter |
SQLException | Database error | Check storage permissions |
KeyPermanentlyInvalidatedException | Device unlock invalidated key | Re-register tenant |
IOException | Network or storage I/O error | Retry operation |
Proper Error Handling Pattern
fynoTotp.getTotp(tenantId) { result ->
result.onSuccess { otp ->
// Handle success
displayOtp(otp)
}
result.onFailure { error ->
when (error) {
is KeyPermanentlyInvalidatedException -> {
// Device was unlocked and key was invalidated
Toast.makeText(context, "Please re-enroll", Toast.LENGTH_SHORT).show()
revokeTenant(tenantId)
}
is InvalidKeyException ->
Security Considerations
Encryption
- Storage: TOTP secrets are encrypted using Android KeyStore before being saved to the database
- Algorithm: Uses Android's default encryption (AES-GCM on API 23+)
- Key Generation: Hardware-backed keys when available
- IV (Initialization Vector): Unique IV generated for each secret and stored separately
Key Management
- Secrets are never stored in plain text
- Keys are stored with
PURPOSE_DECRYPTandPURPOSE_ENCRYPTonly - Biometric authentication can be enforced per-key for additional security
- KeyStore integration requires API 23+
Best Practices
- Always check for null results when calling
getTotp()for inactive tenants - Handle
KeyPermanentlyInvalidatedExceptionafter device unlock changes - Use HTTPS only for tenant registration communication (not handled by SDK)
- Implement timeout logic for OTP input validation
- Do not log or share TOTP secrets in production code
- Regularly audit tenant revocations and enrollments
- Test on both physical and emulated devices for encryption behavior
API Level Considerations
- API < 23: KeyStore encryption unavailable; SDK requires API 23+ for full functionality
- API 23-27: Basic KeyStore support
- API 28+: Enhanced KeyStore features and performance
- API 30+: Biometric integration possible
Database Security
- Database is stored in app-private storage directory
- No sensitive data is cached in SharedPreferences
- All queries use parameterized statements to prevent SQL injection
Changelog
Version 1.0.0
- Initial release
- Core TOTP generation (SHA1, SHA256, SHA512)
- Tenant enrollment and revocation
- Secure secret storage using Android KeyStore
- Support for customizable TOTP parameters
- API 23+ support
Support & Documentation
For additional support, refer to:
- Android KeyStore documentation
- TOTP RFC 6238 specification
- Fyno SDK integration guides
Last Updated: January 2026
SDK Version: 1.0.0
Min API Level: 23
Target API Level: 33+