Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/JonathanHerSa/xolo-api-hub/llms.txt

Use this file to discover all available pages before exploring further.

An API client used for real work often holds sensitive production credentials — Bearer tokens, OAuth client secrets, API keys, and Basic Auth passwords. If a device is left unlocked or passed to another person, those secrets are one tap away. Xolo’s biometric lock addresses this by requiring Face ID, fingerprint, or another device-native biometric before the app becomes usable after it resumes from the background or restarts from a cold start.

Enabling Biometric Lock

Navigate to Settings → Security and toggle Biometric Lock. Before enabling it, Xolo checks that the device actually supports biometric authentication by querying BiometricService.isAvailable. If the device has no enrolled biometrics, the toggle remains disabled. Once enabled, the lock state and its lock delay are persisted in flutter_secure_storage under the keys biometric_enabled and lock_delay, so the setting survives app restarts without touching the SQLite database. You can also configure the lock delay — the number of seconds the app must be in the background before it locks on resume. The default delay is 30 seconds.
// Reading and writing the lock delay (in seconds)
await biometricService.setLockDelay(15);
final delay = await biometricService.getLockDelay(); // returns 15

BiometricService

BiometricService is backed by the local_auth package and provides a minimal, safe API surface over the platform’s native biometric stack.

isAvailable

Future<bool> get isAvailable async
Returns true if the device reports either canCheckBiometrics or isDeviceSupported(). Catches PlatformException and returns false so callers never need to handle platform errors themselves. On platforms where biometrics are unavailable (such as Linux desktop), this consistently returns false.

authenticate()

Future<bool> authenticate({
  required String reason,
  bool biometricOnly = false,
})
Presents the system biometric prompt with the provided reason string (shown to the user as the unlock rationale). Returns true if the user successfully authenticates, false otherwise. If authentication is already in progress (_isAuthenticating == true), the method returns false immediately to prevent overlapping prompts. The persistAcrossBackgrounding flag is set to true so the prompt survives brief backgrounding.

cancelAuthentication()

Future<void> cancelAuthentication()
Calls LocalAuthentication.stopAuthentication() to dismiss any in-progress biometric prompt and resets the internal _isAuthenticating flag.

shouldLockApp()

Future<bool> shouldLockApp({int? forceDelaySeconds})
Determines whether the app should show the lock screen when it returns to the foreground. Returns true only when biometric lock is enabled, the device supports biometrics, and the time elapsed since markAppBackgrounded() was called meets or exceeds the configured lockDelay.

Biometric Lock Screen

When the app resumes from the background and BiometricService.shouldLockApp() returns true, the isAppLockedProvider state is set to true and Xolo overlays the entire UI with BiometricLockScreen. BiometricLockScreen auto-triggers the system biometric prompt on mount (via initState) through an internal _bootstrap() call. If the device has become unavailable since the setting was last saved (e.g. the user removed their enrolled fingerprint), BiometricService.disableIfUnavailable() automatically turns off the lock and lets the user through. If biometric authentication fails, an error message is shown and a manual Unlock button allows the user to retry the prompt. On a successful authentication, isAppLockedProvider is set to false and the lock screen is dismissed, returning the user to whatever screen they were on.
Biometric lock requires a device with enrolled biometrics. If no biometrics are enrolled, or if the device does not support hardware biometrics at all, the setting is automatically disabled and the lock screen will not appear. This check runs both when the setting is first toggled and every time the app attempts to lock.

Credential Storage

All authentication secrets managed by Xolo — Bearer tokens, OAuth 2.0 client secrets and access tokens, API key values, and Basic Auth passwords — are stored exclusively in flutter_secure_storage. The shared instance is configured with AndroidOptions() so Android uses the Android Keystore backend.
// The shared secure storage instance used throughout the app
const kSecureStorage = FlutterSecureStorage(aOptions: AndroidOptions());
SecurityService wraps this storage with three operations used by the rest of the codebase:
// Persisting a secret
await securityService.saveSecure('my-key', 'my-secret-value');

// Reading a secret
final value = await securityService.readSecure('my-key');

// Removing a secret
await securityService.deleteSecure('my-key');
The SQLite database only ever holds opaque reference strings (prefixed secure_auth_ref:) — never raw credentials. See the Auth Overview for details on how AuthSecretService manages this indirection.

Security Profiles

SecurityProfileService lets you choose one of three pre-defined security postures that control app-wide behaviour. The active profile is persisted via XoloRepository.setSetting() / getSetting() under the key security_profile.
ProfilehideSensitiveValuesautoLockOnResumeLock DelayconfirmBeforeExport
standardfalsefalse30 sfalse
hardenedtruetrue15 strue
paranoidtruetrue0 strue
  • hideSensitiveValues — when true, token and password fields in auth forms are always obscured, regardless of any individual field settings.
  • autoLockOnResume — when true, the biometric lock overlay is shown every time the app returns from the background (subject to the lock delay).
  • recommendedLockDelaySeconds — the suggested delay before locking; 0 means lock immediately on every resume.
  • confirmBeforeExport — when true, Xolo shows a confirmation dialog before exporting any collection or environment data.
// Reading the current profile
final profile = await securityProfileService.getProfile();

// Updating the profile
await securityProfileService.setProfile(SecurityProfile.hardened);

// Fetching the full policy for a given profile
final policy = securityProfileService.policyFor(SecurityProfile.paranoid);
print(policy.recommendedLockDelaySeconds); // 0

AES-256 Encryption

EncryptionService provides AES-256-CBC encryption for backup payloads exported from Xolo. It is independent of the biometric lock but forms part of the overall security story: even if a backup file is exfiltrated, its contents remain unreadable without the password. Key derivation uses PBKDF2-HMAC-SHA256 with 120,000 rounds, a random 16-byte salt, and a 32-byte output key. Each encryption operation also generates a fresh 16-byte IV and appends a 32-byte HMAC-SHA256 tag to the payload for authenticated encryption (encrypt-then-MAC). The on-disk format is:
v2:<base64( salt[16] || iv[16] || ciphertext || hmac[32] )>
Decryption re-derives the key from the provided password and salt, verifies the HMAC using a constant-time comparison before attempting decryption, and throws if the MAC is invalid.
final encryptionService = ref.read(encryptionServiceProvider);

// Encrypt a backup payload
final encrypted = encryptionService.encryptString(jsonPayload, password);
// encrypted == "v2:AAAA...base64..."

// Decrypt
final plain = encryptionService.decryptString(encrypted, password);
Keys are never stored anywhere — they are derived at runtime from the user-supplied password, which is never persisted by Xolo.
For maximum security, combine biometric lock with the Paranoid security profile when testing on shared or untrusted networks. The Paranoid profile locks immediately on every app resume, hides all sensitive values in the UI, and requires confirmation before any data export.

Build docs developers (and LLMs) love