Conversation
First I want to thank you for this nice plugin, saved me A LOT of time I think :) I needed custom prompts for different actions, because in my App specific actions are happening with them and I want to communicate this clearly to the user. Hope you will like it too. Cheers
This is more expressiv I think.
|
Now with 100% more null-safety ... |
|
@rokk4 sorry for the late response. Out of curiosity, do you just have different prompts at different locations inside your app, or do you need to literally have a different prompt on every read call (ie. dynamically creating it with some variables). I'm thinking if it wouldn't it be easier to just have something like I like that |
|
@hpoul thanks for the reply right after breakfast :) I'll show you how I put it to use and than try to explain why prompt customization should be part of the simple helper api IMHO and why more instances are not so desirable. // auth_bloc.dart
@injectable
class AuthBloc extends Bloc<AuthEvent, AuthState> {
...
final IEncryptionKeyStorage _encryptionKeyStorage;
...
@override
Stream<AuthState> mapEventToState(AuthEvent event) async* {
yield* event.map(
appStarted: (e) async* {
await _encryptionKeyStorage.init();
yield* _getEncryptionKeyAndUnlockDB();
},
...
);
}
Stream<AuthState> _getEncryptionKeyAndUnlockDB() async* {
final unlockFailureOrEncryptionKey =
await _encryptionKeyStorage.unlockEncryptionKey();
yield* unlockFailureOrEncryptionKey.fold(
(unlockFailure) async* {
// Generate Key and try again.
if (unlockFailure == const UnlockFailure.noEncryptionKeyPresent()) {
await _encryptionKeyStorage.generateEncryptionKey();
yield* _getEncryptionKeyAndUnlockDB();
} else {
yield const AuthState.errorUnlockingDB();
}
},
(encryptionKey) async* {
await _localDB.unlockDB(encryptionKey);
yield const AuthState.dbUnlocked();
},
);
}
}// encryption_key_storage_impl.dart
@LazySingleton(as: IEncryptionKeyStorage)
class EncryptionKeyStorageImpl implements IEncryptionKeyStorage {
static const String readPromptTitle = 'Datenbank entschlüsseln';
static const String readPromptSubtitle =
'Öffnen Sie das biometrische Schloss der lokalen Datenbank.';
static const String writePromptTitle = 'Verschlüsselte Datenbank erstellen';
static const String writePromptSubtitle =
'Erstelle Sie mit Ihrem biometrischem Zugang eine verschlüsselte, lokale Datenbank.';
static const String writePromptDescription =
'Alle Ihre Daten werden sicher verschlüsselt auf Ihrem Gerät gespeichert und können nur durch Sie persönlich zugänglich gemacht werden.';
static const String negativeButton = 'Abbrechen';
static const AndroidPromptInfo readPrompt = AndroidPromptInfo(
title: readPromptTitle,
subtitle: readPromptSubtitle,
negativeButton: negativeButton,
);
static const AndroidPromptInfo writePrompt = AndroidPromptInfo(
title: writePromptTitle,
subtitle: writePromptSubtitle,
description: writePromptDescription,
negativeButton: negativeButton,
);
static const String storageFileName = 'encryptionKeyStorage';
final storageFileInitOptions = StorageFileInitOptions(
authenticationValidityDurationSeconds: 30,
authenticationRequired: !Platform.isLinux,
);
BiometricStorageFile encryptionKeyStorage;
@override
Future<void> init() async {
encryptionKeyStorage = await BiometricStorage().getStorage(
storageFileName,
options: storageFileInitOptions,
);
}
@override
Future<Either<UnlockFailure, List<int>>> unlockEncryptionKey() async {
try {
return optionOf(
await encryptionKeyStorage.read(
perActionPromptInfo: readPrompt,
),
).fold(
() => const Left(
UnlockFailure.noEncryptionKeyPresent(),
),
(encryptionKeyString) => Right(
base64Decode(encryptionKeyString),
),
);
} on AuthException {
return const Left(
UnlockFailure.bioAuthCanceledByUser(),
);
} on PlatformException {
return const Left(
UnlockFailure.authNotSetupOnPlatform(),
);
}
}
@override
Future<void> generateEncryptionKey() async {
final newEncryptionKey = Hive.generateSecureKey();
await encryptionKeyStorage.write(
base64Encode(newEncryptionKey),
perActionPromptInfo: writePrompt,
);
}
}At one time values are written, on creation of the DB (first run // key rotation by user), and then again read (normal startup). Showing the same prompt but with two different actions happening is a huge UX anti-pattern IMHO. Using BiometricsStorage directly on the other hand would not be so clean, because it absolutely makes sense to have a helper class to operate on specific files only. Since the But maybe I am just holding it wrong 😅️ |
well, it always starts with one parameter :-) and the second parameter is just like the first one, so why not add it as well.. ;-) especially since the iOS/MacOS equivalent is still missing.. which is hard coded right now. wouldn't it be better for your use case to pass in a I think if it was just one platform passing it directly into the method might be the easiest way, but dealing with all platforms for each read/write/delete call seems cumbersome |
|
Is this getting merged any time soon? I'm looking towards this too. Sometimes I want to encrypt something without the user authentication, but read it with the user authentication. Since |
|
|
First I want to thank you for this nice plugin, saved me A LOT of time I think :)
I needed custom prompts for different actions, because in my App specific actions are happening with them and I want to communicate this clearly to the user.
Hope you will like it too.
Cheers