Dart: Push, attestation and crashes
Register devices for push, prove requests come from your real app, and report crashes with the Dart and Flutter SDK.
Register devices for push, prove requests come from your real app, and report crashes. Every call runs as the client's signed-in user, so your project's policies and row-level security apply. Types and members are listed in source order; each one links to its HTTP endpoint where it maps to one.
#Types and members
SovariaPush
Push notifications for the signed-in app user.
Get the token from your push plugin and register it after sign-in:
Sending happens on your server or in a function with the service role key (api.push.send), never from the app.
class SovariaPushSovariaPush.SovariaPush
Create an instance.
SovariaPush(this._client)SovariaPush.register
Register (or move to this user) a push token.
Future<PushDevice> register({required String token, required String platform, String? provider, String? appId,
List<String>? topics, String? locale})provider is fcm or apns (default: apns for a raw hex iOS token, else fcm). The token is tied to the client's Sovaria.device.
SovariaPush.unregister
Drop a token (sign-out, notifications turned off).
Future<void> unregister(String token)Safe to call twice.
SovariaPush.devices
Future<List<PushDevice>> devices()SovariaPush.subscribe
Future<PushDevice> subscribe(String deviceRowId, List<String> topics)SovariaPush.unsubscribe
Future<PushDevice> unsubscribe(String deviceRowId, List<String> topics)PushDevice
class PushDevicePushDevice.PushDevice
Create an instance.
PushDevice(this.id, this.platform, this.provider, this.topics, this.raw)PushDevice.id
A property.
final String idPushDevice.platform
A property.
final String platformPushDevice.provider
A property.
final String providerPushDevice.topics
A property.
final List<String> topicsPushDevice.raw
A property.
final Map<String, Object?> rawPushDevice.PushDevice.fromJson
Create an instance.
factory PushDevice.fromJson(Object? json)AttestationProvider
Produces platform evidence for a challenge. Implement it with your platform code (a MethodChannel to Play Integrity / App Attest), or use DebugAttestationProvider on simulators.
Return the fields of POST /api/v1/attest/exchange: * Play Integrity: {'provider': 'play_integrity', 'app_id': packageName, 'token': integrityToken} (request the token with nonce/requestHash = the challenge); * App Attest, first time: {'provider': 'app_attest', 'app_id': bundleId, 'key_id': keyId, 'attestation': base64(attestationObject)} with clientDataHash = SHA-256(challenge); afterwards {'provider': 'app_attest', 'app_id': bundleId, 'key_id': keyId, 'assertion': base64(assertion)}; * DeviceCheck: {'provider': 'device_check', 'app_id': bundleId, 'token': base64(deviceToken)}.
abstract class AttestationProviderAttestationProvider.evidence
Future<Map<String, Object?>> evidence(String challenge)DebugAttestationProvider
Stands in for a device on simulators and CI. Create the token in the console (Mobile › App attestation › Debug tokens); never ship it in a release build.
class DebugAttestationProvider implements AttestationProviderDebugAttestationProvider.DebugAttestationProvider
Create an instance.
DebugAttestationProvider(this.debugToken)DebugAttestationProvider.debugToken
A property.
final String debugTokenDebugAttestationProvider.evidence
Future<Map<String, Object?>> evidence(String challenge)AttestationManager
Keeps a valid attestation token and refreshes it before it expires. The client sends it as X-Sovaria-Attestation and fetches a new one after a 401 APP_ATTESTATION_REQUIRED.
class AttestationManagerAttestationManager.AttestationManager
Create an instance.
AttestationManager(this._client, this.provider, {this.margin = const Duration(seconds: 60)})AttestationManager.provider
A property.
final AttestationProvider providerAttestationManager.margin
A property.
final Duration marginAttestationManager.token
The current token (fetching one first when there is none or it is about to expire).
Future<String?> token()Returns null when attestation fails, so requests still go out (the project may only monitor).
AttestationManager.refresh
Fetch a new token now (one request at a time).
Future<String?> refresh()AttestationManager.clear
void clear()SovariaCrashes
App crash reporting (Monitor › Crashes in the console), the same reports as the Swift and Kotlin clients: error type, message, stack, release, platform, device, the last 50 breadcrumbs and the signed-in user (the server takes the user from the request token).
In a Flutter app, route the framework's and the engine's errors here once at start-up:
or wrap runApp in runZonedGuarded(..., sovaria.crashes.recordFatal). A fatal report is written to the OfflineStore first and sent on the next launch, because a dying isolate may not finish a request.
class SovariaCrashesSovariaCrashes.SovariaCrashes
Create an instance.
SovariaCrashes(this._client)SovariaCrashes.installed
A property.
bool get installedSovariaCrashes.install
Start capturing.
void install({String? release, String? environment, OfflineStore? store})Pass a durable store (or enable offline first) so a fatal crash survives to the next launch.
SovariaCrashes.uninstall
void uninstall()SovariaCrashes.addBreadcrumb
Add a breadcrumb (a step that led up to a crash).
void addBreadcrumb(String category, String message)SovariaCrashes.capture
Report a handled error.
bool capture(Object error, {StackTrace? stack, bool handled = true, Map<String, String> tags = const {},
Map<String, Object?> extra = const {}, String? fingerprint})Returns false if crash reporting isn't installed.
SovariaCrashes.recordFatal
An error that ends the app: persisted first (sent on the next launch), then sent now on a best-effort basis.
Future<void> recordFatal(Object error, StackTrace? stack)SovariaCrashes.flush
Send queued reports now (joins a flush already running).
Future<void> flush()What didn't go through is kept (bounded).