RAuth Flutter SDK v2
Cross-platform Flutter library for RAuth v2 reverse authentication. Supports WhatsApp, Reverse SMS, OneID, and Passkey authentication methods.
Features
- ✅ Multiple Auth Methods: WhatsApp, Reverse SMS, OneID, Passkey
- ✅ Cross-Platform: Android, iOS, and Web support
- ✅ Auto Device Detection: Automatic device info and IP detection
- ✅ HMAC Signing: Secure mobile authentication
- ✅ Start Verification: Automatically opens verification intents/deeplinks
- ✅ Await Verification: Automatic session verification
- ✅ Session State Management: Session token and phone stored internally - no need to pass repeatedly
- ✅ Passkey Support: WebAuthn passkey registration with biometric authentication
- ✅ Simplified Passkey API: One-line passkey registration and approval with automatic challenge-response handling
- ✅ Approval System: Request approval tokens for sensitive actions with passkey signature verification
Installation
Add to your pubspec.yaml:
dependencies:
rauth_flutter: ^2.0.0
Then run:
flutter pub get
Quick Start
1. Initialize RAuth
import 'package:rauth_flutter/rauth_flutter.dart';
void main() {
Rauth.init(RauthConfig(
appId: 'your-app-id',
appSecret: 'your-app-secret',
debug: false, // Optional, enable debug logs
));
runApp(MyApp());
}
2. Start Authentication Session
// Start session with WhatsApp
final session = await Rauth.instance.startSession(
phone: '+1234567890',
loginMethod: LoginMethod.whatsapp,
);
// Get QR code for display
final qrCode = session.getQR(); // Automatically gets QR from any method
// Display QR code in your UI
3. Start Verification
// Automatically opens the appropriate verification method
// - WhatsApp: Shows native chooser if both apps available, otherwise opens directly
// - SMS:
// - If permissions granted: Sends SMS automatically in background (with SIM selection on dual SIM)
// - If permissions not granted: Opens SMS app for manual sending
// - OneID: Opens deeplink directly
await session.startVerification();
4. Await Verification
try {
// ✅ No need to pass sessionToken & phone - stored internally!
final status = await session.awaitVerification();
// Session verified!
print('Verified via: ${status.verifiedVia}');
// Send session token to your backend
final jwt = await yourBackend.authenticate(
sessionToken: status.token,
phone: status.phone,
);
} on TimeoutError {
print('Verification timeout');
} on SessionRevokedError {
print('Session was revoked');
}
Note: The session token and phone number are automatically stored when you call startSession(). You don't need to pass them again!
Complete Example
Login Flow
import 'package:rauth_flutter/rauth_flutter.dart';
class AuthService {
Future<void> loginWithWhatsApp(String phone) async {
try {
// 1. Initialize session
final session = await Rauth.instance.startSession(
phone: phone,
loginMethod: LoginMethod.whatsapp,
);
// 2. Show QR code
final qrCode = session.getQR(); // Automatically gets QR from any method
// Display QR code in UI
// 3. Start verification (opens WhatsApp/SMS/OneID)
await session.startVerification();
// 4. Await verification (sessionToken & phone stored internally)
final status = await session.awaitVerification();
// 4. Send to backend
// NOTE: If you are doing signup, you can include additional fields
// like username, dob, gender along with phone and session_token.
final jwt = await http.post(
Uri.parse('https://your-api.com/auth/login'),
body: jsonEncode({
'session_token': status.token,
'phone': status.phone,
// Optional (for signup):
// 'username': 'john_doe',
// 'dob': '1995-08-20',
// 'gender': 'male',
}),
);
// 5. Store JWT and proceed
await saveJWT(jwt);
} catch (e) {
if (e is TimeoutError) {
print('Verification timeout');
} else if (e is SessionRevokedError) {
print('Session revoked');
} else {
print('Error: $e');
}
}
}
}
Passkey Support
RAuth Flutter SDK supports native WebAuthn/Passkeys across all platforms for secure approval flows. Passkeys provide phishing-resistant authentication using device biometrics (fingerprint, face recognition) or PIN.
Platform Support
| Platform | Minimum Version | Native API | Sync Support |
|---|---|---|---|
| Web | All modern browsers | WebAuthn API | Browser-based |
| iOS | iOS 16.0+ | Passkeys API (ASAuthorizationController) | iCloud Keychain sync |
| Android 14+ | Android 14+ | CredentialManager API | Google Password Manager sync |
| Android 9-13 | Android 9+ | Fido2ApiClient (Google Play Services) | Google Password Manager sync |
Note: On older Android versions (below 9) or if native APIs are unavailable, the library automatically falls back to a secure custom implementation.
Quick Start: Passkey Registration & Approval
Simplified API - Everything handled automatically!
// After session is verified
final session = await Rauth.instance.startSession(
phone: '+1234567890',
loginMethod: LoginMethod.whatsapp,
);
await session.startVerification();
final status = await session.awaitVerification();
// 1. Register passkey (one line!)
// ✅ Automatically prompts for biometric/PIN
// ✅ Works on Web, iOS 16+, Android 9+
await session.registerPasskeyWithBiometric();
// 2. Request approval token with passkey (one line!)
// ✅ Automatically handles challenge-response flow
// ✅ Prompts for biometric authentication
final approval = await session.requestApprovalTokenWithPasskey(
action: ApprovalAction.transfer,
amount: 1000.50,
description: 'Transfer to John Doe',
metadata: {
'recipient': 'john.doe@example.com',
'account_number': '1234567890',
},
);
// 3. Use approval token in your backend request
final transferResponse = await http.post(
Uri.parse('https://your-api.com/transfer'),
headers: {
'Authorization': 'Bearer $jwt',
'X-Approval-Token': approval.approvalToken,
},
body: jsonEncode({
'amount': 1000.50,
'recipient': 'john.doe@example.com',
}),
);
What Happens Automatically
The library handles everything for you:
- ✅ Biometric prompts: Native platform UI (Face ID, Touch ID, fingerprint, PIN)
- ✅ Challenge-response flow: Two-step approval with automatic retry
- ✅ Platform-specific APIs: Uses native WebAuthn on each platform
- ✅ Session management: No need to pass sessionToken/phone repeatedly
- ✅ Error handling: Automatic fallback if native APIs unavailable
Platform-Specific Behavior
Web
- Uses browser's WebAuthn API
- Shows native browser passkey dialog
- Works on Chrome, Safari, Firefox, Edge
- Passkeys sync via browser's password manager
iOS 16+
- Uses native Passkeys API
- Shows iOS system passkey dialog
- Requires Face ID, Touch ID, or device passcode
- Passkeys sync via iCloud Keychain across Apple devices
Android 14+
- Uses CredentialManager API
- Shows Google Password Manager passkey dialog
- Requires fingerprint, face unlock, or device PIN
- Passkeys sync via Google Password Manager
Android 9-13
- Uses Fido2ApiClient (via Google Play Services)
- Shows Google Password Manager passkey dialog
- Requires fingerprint, face unlock, or device PIN
- Passkeys sync via Google Password Manager
- Note: Requires Google Play Services to be installed
Complete Example: Bank Transfer with Passkey
class TransferService {
Future<void> transferMoney({
required double amount,
required String recipient,
}) async {
try {
// Request approval with passkey
final approval = await Rauth.instance.requestApprovalTokenWithPasskey(
action: ApprovalAction.transfer,
amount: amount,
description: 'Transfer to $recipient',
metadata: {'recipient': recipient},
requireBiometric: true,
localizedReason: 'Authenticate to complete transfer',
);
// Execute transfer with approval token
await http.post(
Uri.parse('https://your-api.com/transfer'),
headers: {
'Authorization': 'Bearer $jwt',
'X-Approval-Token': approval.approvalToken,
},
body: jsonEncode({
'amount': amount,
'recipient': recipient,
}),
);
print('Transfer successful!');
} catch (e) {
if (e.toString().contains('Passkey not registered')) {
// Auto-register passkey if not registered
await Rauth.instance.registerPasskeyWithBiometric(
localizedReason: 'Register passkey for secure transfers',
);
// Retry transfer
await transferMoney(amount: amount, recipient: recipient);
} else {
print('Transfer failed: $e');
}
}
}
}
API Reference
Rauth.init()
Initialize RAuth with configuration.
Rauth.init(RauthConfig(
appId: 'your-app-id',
appSecret: 'your-app-secret',
debug: false, // Optional, enable debug logs
));
Parameters:
appId: Your RAuth app ID from dashboardappSecret: Your RAuth app secret for HMAC signingdebug: Enable debug logging (default:false)
Rauth.instance.startSession()
Start a new authentication session.
Future<SessionInitResponse> startSession({
required String phone,
required LoginMethod loginMethod,
String? ipOverride,
String? locationOverride,
String? deviceNameOverride,
String? browserOverride,
String? platformOverride,
String? deviceFingerprintOverride,
})
Parameters:
phone: Phone number in international format (e.g., "+1234567890")loginMethod:LoginMethod.whatsapp,LoginMethod.reverseSms,LoginMethod.oneid, orLoginMethod.passkey- Device info overrides: Optional parameters to override auto-detected device info
Returns: SessionInitResponse with sessionToken and verificationLinks
Note: The session token and phone number are automatically stored internally for use in subsequent calls.
session.startVerification()
Start verification by opening the appropriate intent/deeplink. This method automatically detects the login method and handles the verification flow.
Future<void> startVerification()
No parameters needed! Works automatically based on session data.
Behavior:
- WhatsApp: Shows native Android chooser if both regular/business deeplinks available, otherwise opens directly
- SMS:
- If app has
SEND_SMSandREAD_PHONE_STATEpermissions: SMS is sent automatically in background with SIM selection dialog (on dual SIM devices) - If permissions not granted: Opens default SMS app with pre-filled phone number and message (user sends manually)
- If app has
- OneID: Directly opens deeplink
Note: No phone parameter needed - only uses deeplinks/tokens from session. No extra setup required - works out of the box!
Session State Management
The library automatically stores session state when you call startSession()!
- ✅
sessionTokenandphoneare stored internally - ✅ No need to pass them repeatedly in subsequent calls
- ✅ All methods can use stored state automatically
- ✅ Optional parameters allow override if needed
session.awaitVerification() (Recommended)
Await session verification using stored session state.
Future<SessionStatusResponse> awaitVerification({
int interval = 1,
int timeout = 900,
})
No parameters needed! Uses session state stored by startSession().
Returns: SessionStatusResponse when verified
Throws:
TimeoutError: If timeout reachedSessionRevokedError: If session is revokedSessionNotVerifiedError: If session expired
Rauth.instance.awaitVerification() (Alternative)
Await session verification with optional parameters.
Future<SessionStatusResponse> awaitVerification({
String? sessionToken, // Optional - uses stored if not provided
String? phone, // Optional - uses stored if not provided
int interval = 1,
int timeout = 900,
})
Parameters:
sessionToken: Optional - uses stored session token if not providedphone: Optional - uses stored phone if not providedinterval: Polling interval in seconds (default: 1)timeout: Maximum time to wait in seconds (default: 900)
Rauth.instance.checkSessionStatus()
Check session status once.
Future<SessionStatusResponse> checkSessionStatus({
String? sessionToken, // Optional - uses stored if not provided
String? phone, // Optional - uses stored if not provided
})
session.registerPasskeyWithBiometric() (Recommended)
Register a passkey with biometric authentication - handles everything automatically!
Future<PasskeyRegistrationResponse> registerPasskeyWithBiometric({
String? localizedReason,
})
What it does:
- ✅ Prompts for biometric authentication (Face ID, Touch ID, fingerprint, PIN)
- ✅ Uses native WebAuthn APIs on each platform
- ✅ Registers passkey with backend automatically
- ✅ Uses stored session state automatically
Parameters:
localizedReason: Optional reason shown in biometric prompt (e.g., "Register passkey for secure transfers")
No sessionToken/phone needed! Uses session state stored by startSession().
Platform behavior:
- Web: Shows browser's native passkey registration dialog
- iOS 16+: Shows iOS system passkey dialog with Face ID/Touch ID
- Android 14+: Shows Google Password Manager passkey dialog
- Android 9-13: Shows Google Password Manager passkey dialog (requires Google Play Services)
Rauth.instance.registerPasskeyWithBiometric() (Alternative)
Register a passkey with optional session override.
Future<PasskeyRegistrationResponse> registerPasskeyWithBiometric({
String? sessionToken, // Optional - uses stored if not provided
String? phone, // Optional - uses stored if not provided
String? localizedReason,
})
Note: Passkey registration is mandatory before requesting approval tokens. The library automatically registers passkey if not already registered when calling requestApprovalTokenWithPasskey().
session.requestApprovalTokenWithPasskey() (Recommended)
Request approval token with passkey - handles challenge-response automatically!
Future<ApprovalResponse> requestApprovalTokenWithPasskey({
required ApprovalAction action,
double? amount,
String? description,
Map<String, dynamic>? metadata,
bool requireBiometric = true,
String? localizedReason,
})
What it does:
- ✅ Auto-registers passkey if not already registered
- ✅ Prompts for biometric authentication (Face ID, Touch ID, fingerprint, PIN)
- ✅ Requests approval from backend (may receive challenge)
- ✅ Signs challenge automatically using native WebAuthn
- ✅ Retries with signature automatically
- ✅ Uses stored session state automatically
Parameters:
action: Approval action type (transfer, withdraw, deleteAccount, etc.)amount: Optional amount for financial actionsdescription: Optional descriptionmetadata: Optional metadata maprequireBiometric: Whether to require biometric (default:true)localizedReason: Optional reason shown in biometric prompt
Returns: ApprovalResponse with approvalToken that expires in 15 minutes
No sessionToken/phone needed! Uses session state stored by startSession().
Platform behavior:
- Web: Uses browser's WebAuthn API for challenge signing
- iOS 16+: Uses iOS Passkeys API with Face ID/Touch ID
- Android 14+: Uses CredentialManager API with fingerprint/face unlock
- Android 9-13: Uses Fido2ApiClient with fingerprint/face unlock
Rauth.instance.requestApprovalTokenWithPasskey() (Alternative)
Request approval token with optional session override.
Future<ApprovalResponse> requestApprovalTokenWithPasskey({
String? sessionToken, // Optional - uses stored if not provided
String? phone, // Optional - uses stored if not provided
required ApprovalAction action,
double? amount,
String? description,
Map<String, dynamic>? metadata,
bool requireBiometric = true,
String? localizedReason,
})
Approval Actions:
ApprovalAction.transfer- Money transferApprovalAction.withdraw- Withdrawal requestApprovalAction.deleteAccount- Account deletionApprovalAction.changePassword- Password changeApprovalAction.sessionVerification- Session verification
Permissions (Android)
The following permissions are required for automatic SMS verification and are included in the library's AndroidManifest.xml:
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Permission Behavior for SMS Verification
If both SEND_SMS and READ_PHONE_STATE permissions are granted:
- ✅ SMS is sent automatically in the background
- ✅ On dual SIM devices: Shows native SIM selection dialog, then sends SMS from selected SIM
- ✅ On single SIM devices: Sends SMS directly
- ✅ Seamless user experience (like UPI apps)
If permissions are not granted:
- 📱 Opens default SMS app with pre-filled phone number and message via
ACTION_SENDTOintent - 👤 User manually sends the SMS
- ✅ Works without any permissions (fallback to manual SMS)
Note: The library automatically detects permissions and adapts behavior. No additional configuration needed!
Device Info Auto-Detection
The SDK automatically detects device information:
-
Mobile (Android/iOS):
device_name: Device model (e.g., "iPhone 15", "Pixel 7")platform: OS version (e.g., "iOS 17", "Android 14")browser: Empty (not applicable)ip: Auto-fetched public IP
-
Web:
device_name: nullplatform: OS name (e.g., "Windows 11", "macOS", "Linux")browser: Browser name + version (e.g., "Chrome 120")ip: Auto-fetched public IP
All fields can be overridden using optional parameters in startSession().
Error Handling
try {
await Rauth.instance.startSession(...);
} on NetworkError catch (e) {
// Network/connection issues
} on AuthError catch (e) {
// Authentication failed (401, 403)
} on ValidationError catch (e) {
// Invalid input (400, 402)
} on TimeoutError catch (e) {
// Verification timeout
} on SessionRevokedError catch (e) {
// Session was revoked
} on RauthException catch (e) {
// Other RAuth errors
}
Platform Support
Authentication Methods
- ✅ Android (9+)
- ✅ iOS (12+)
- ✅ Web (Chrome, Safari, Firefox, Edge)
Passkey Support
- ✅ Web: All modern browsers (WebAuthn API)
- ✅ iOS: iOS 16.0+ (Native Passkeys API)
- ✅ Android 14+: CredentialManager API
- ✅ Android 9-13: Fido2ApiClient (requires Google Play Services)
- ✅ Fallback: Custom secure implementation for older devices
License
MIT
Support
For issues and questions, visit https://rauth.io or contact support@rauth.io