SIMA Flutter Plugin
A Flutter plugin that enables secure digital signature and authentication flows using the SIMA mobile application.
This package provides a clean, reusable, and production-ready interface for launching the SIMA app from Flutter, sending a cryptographic challenge, and receiving the signed response back into your application.
🔐 All SIMA-specific native logic is encapsulated inside the plugin
🧼 The Flutter app layer stays clean and platform-agnostic
🔗 Package Links
- 📦 pub.dev: https://pub.dev/packages/sima
- 📄 License: MIT
- 🛠 Platforms: Android, iOS
✨ Features
- Secure challenge–response signing flow
- Native SIMA app integration
- Android & iOS support
- Minimal and clean Flutter API
- Asset-based logo handling (path only)
- No Base64 handling required in Flutter
- Plugin-managed native callbacks
- Production-ready architecture
📦 Installation
Add the package from pub.dev:
dependencies:
sima: ^1.0.1
Then run:
flutter pub get
🔐 Important Credentials Notice
Before integrating SIMA, you must obtain the following credentials:
- clientId
- masterKey
These credentials are NOT generated by the plugin.
⚠️ Both
clientIdandmasterKeymust be provided by SIMA platform developers.
They uniquely identify your application and are required for signature validation.
Security Rule
clientId→ can be safely used in the mobile applicationmasterKey→ MUST NEVER be stored in Flutter or mobile apps
It must be stored and used only on your backend.
🧠 Overall Secure Authentication Flow (loginSafe)
The recommended authentication flow uses the loginSafe() method
and follows a clear separation of responsibilities between
the mobile app, backend, and SIMA app.
🔁 Step-by-Step Flow
1️⃣ Mobile App (Flutter)
- Generates a secure random challenge using:
final challenge = Sima.createChallenge(); - Sends the Base64-encoded challenge to your backend.
2️⃣ Backend (Your Server)
- Receives the challenge from the mobile app
- Uses the masterKey (provided by SIMA) to generate an HMAC signature
- Optionally:
- Stores the challenge temporarily
- Marks it as used after successful authentication (anti-replay protection)
- Returns the generated signature to the mobile app
3️⃣ Mobile App (Flutter)
- Receives the signature from backend
- Calls:
final response = await Sima.loginSafe(
clientId: 'YOUR_CLIENT_ID',
returnScheme: 'your-app-scheme',
serviceName: 'Your App Name',
logoPath: 'assets/logo.png',
challenge: challenge,
signature: signature,
);
4️⃣ SIMA Mobile Application
- Verifies the signature and challenge
- Prompts the user for authentication (PIN / biometrics)
- Signs the challenge using the user's SIMA certificate
- Redirects back to your app with:
- signed data
- certificate
- status
5️⃣ Final Result (Your App)
- Receives the result through the plugin
- Sends signature + certificate to backend (optional but recommended)
- Backend verifies the signature and completes authentication
🧩 Flow Diagram (Conceptual)
Flutter App
|
|-- createChallenge()
|
|---- challenge ----> Backend
| |
| |-- sign(challenge, masterKey)
| |
|<--- signature -------|
|
|-- loginSafe(challenge, signature)
|
SIMA App
|
|-- user authentication
|-- certificate signing
|
|--> callback
|
Flutter App
🧩 Platform Configuration
To ensure SIMA Flutter Plugin works correctly, a few mandatory platform-specific configuration steps are required for both iOS and Android.
🍎 iOS Configuration
1️⃣ Add URL Scheme (Required)
The SIMA application returns the result back to your app via deep linking. For this to work, you must register a custom URL scheme in your iOS app.
ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>your-app-scheme</string>
</array>
</dict>
</array>
⚠️ The value of your-app-scheme must exactly match the returnScheme
passed to the loginSafe() method.
2️⃣ Forward SIMA Callback to the Plugin
After the SIMA app completes the operation, it redirects back to your app. This callback must be forwarded to the plugin at the AppDelegate level.
ios/Runner/AppDelegate.swift:
override func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
let items = components.queryItems else {
return false
}
var data: [String: String] = [:]
for item in items {
data[item.name] = item.value ?? ""
}
NotificationCenter.default.post(
name: NSNotification.Name("SIMA_CALLBACK"),
object: nil,
userInfo: data
)
return true
}
⚠️ Do not add any business logic here.
This method should only forward the callback to the plugin.
3️⃣ LSApplicationQueriesSchemes (Recommended)
To allow iOS to detect whether the SIMA app is installed,
add the following to Info.plist:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>sima</string>
</array>
🤖 Android Configuration
1️⃣ Package Visibility (Required for Android 11+)
On Android 11 (API 30) and above, apps must explicitly declare which external packages they intend to interact with.
Add the SIMA package name to your manifest:
android/app/src/main/AndroidManifest.xml:
<queries>
<package android:name="az.dpc.sima" />
</queries>
Without this entry, Android will not be able to find the SIMA app.
2️⃣ Internet Permission
If your app communicates with a backend service (e.g. to obtain a signature), the following permission is required:
<uses-permission android:name="android.permission.INTERNET" />
3️⃣ MainActivity
No SIMA-specific native code is required in your activity.
Recommended setup:
class MainActivity : FlutterFragmentActivity()
All intents and callbacks are handled internally by the plugin.
✅ Result
After completing these steps:
- The SIMA app will launch correctly
- Callbacks will be received without issues
- The
loginSafe()flow will work reliably on both iOS and Android
🎨 Asset Setup (Logo)
The SIMA plugin requires a logo image to be displayed inside the SIMA application.
1️⃣ Add the logo to your Flutter assets
flutter:
assets:
- assets/logo.png
Supported formats:
- PNG
- JPG
⚠️ SVG is not supported for SIMA logos.
2️⃣ Pass only the asset path
logoPath: 'assets/logo.png'
The plugin will load and convert the image internally.
🚀 Usage (Secure Example)
final challenge = Sima.createChallenge();
final signature = await signatureProvider.sign(
challenge.toBase64(),
);
ℹ️ Note:
This call represents a network request to your backend service.
The backend generates the signature using the SIMA masterKey and returns it to the mobile app.
The masterKey never exists in Flutter or on the device.
final response = await Sima.loginSafe(
clientId: 'YOUR_CLIENT_ID',
returnScheme: 'your-app-scheme',
serviceName: 'Your App Name',
logoPath: 'assets/logo.png',
challenge: challenge,
signature: signature,
);
❗ Error Handling
| Case | Description |
|---|---|
null response |
Operation cancelled or no callback |
SIMA_NOT_FOUND |
SIMA app not installed |
LOGO_ERROR |
Logo asset not found |
BAD_URL |
iOS deep-link failed |
🔐 Security Notes
- Never hardcode secrets in Flutter UI code
- Store the SIMA masterKey only on backend
- Always validate SIMA responses server-side
- Use anti-replay protection for challenges
⚠️ Legacy API
The login() method is provided only for backward compatibility and demos.
❗ Do NOT use it in production.
Always prefer createChallenge() + loginSafe().
📄 License
MIT License
⚠️ Disclaimer
This plugin is not officially affiliated with SIMA.
The example application uses a mock signature provider. In real applications, the signature must be generated on your backend using your SIMA master key.