getKeyFromUserIfRequired function
Ask for the security key from the user if the security key is not available or cannot be verfied using the verification key stored in PODs.
Implementation
Future<void> getKeyFromUserIfRequired(
BuildContext context,
Widget child,
) async {
if (await KeyManager.hasSecurityKey()) {
return;
} else {
// Get the webId to display in the security key prompt.
final webId = await getWebId();
const inputKey = 'security_key';
final formKey = GlobalKey<FormBuilderState>();
// The security key can only be verified by deriving it (Argon2id for
// version 2 PODs), which is asynchronous and cannot run in a synchronous
// form validator. So the validator only checks the field is non-empty;
// correctness is verified on submit by `KeyManager.setSecurityKey`, which
// throws [SecurityKeyVerificationException] when the key is wrong.
final inputField = (
fieldKey: inputKey,
fieldLabel: 'Security Key',
validateFunc: (key) => (key == null || (key as String).isEmpty)
? 'Please enter a key'
: null,
);
// Use the unified SecurityKeyUI widget with the appropriate configuration.
final securityKeyInput = SecurityKeyUI(
webId: webId,
title: 'Security Key',
message: SecurityStrings.securityKeyPrompt,
inputFields: [inputField],
formKey: formKey,
submitFunc: (formDataMap) async {
try {
await KeyManager.setSecurityKey(formDataMap[inputKey].toString());
} on SecurityKeyVerificationException {
// Wrong key: show an inline error and keep the prompt open.
formKey.currentState?.fields[inputKey]
?.invalidate('Incorrect Security Key');
return;
}
debugPrint('Security key saved');
if (context.mounted) Navigator.pop(context);
},
child: child,
);
if (context.mounted) {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => securityKeyInput),
);
// Notify the global status bar notifier that the key status may have
// changed after the user submitted (or dismissed) the key prompt.
await securityKeyNotifier.refreshStatus();
}
}
}