fix: add digits to setup page and create stream type

This commit is contained in:
Prateek Sunal 2024-05-03 13:56:59 +05:30
parent b52133fe57
commit b374dc0dd0
5 changed files with 63 additions and 15 deletions

View file

@ -20,6 +20,7 @@
"codeIssuerHint": "Issuer",
"codeSecretKeyHint": "Secret Key",
"codeAccountHint": "Account (you@domain.com)",
"digitsAccountHint": "Digits (default=6)",
"accountKeyType": "Type of key",
"sessionExpired": "Session expired",
"@sessionExpired": {

View file

@ -58,18 +58,20 @@ class Code {
updatedAlgo,
updatedType,
updatedCounter,
"otpauth://${updatedType.name}/$updateIssuer:$updateAccount?algorithm=${updatedAlgo.name}&digits=$updatedDigits&issuer=$updateIssuer&period=$updatePeriod&secret=$updatedSecret${updatedType == Type.hotp ? "&counter=$updatedCounter" : ""}",
"otpauth://${updatedType.name}/$updateIssuer:$updateAccount?algorithm=${updatedAlgo.name}"
"${updatedType == Type.steam ? "" : "&digits=$updatedDigits"}&issuer=$updateIssuer"
"&period=$updatePeriod&secret=$updatedSecret${updatedType == Type.hotp ? "&counter=$updatedCounter" : ""}",
generatedID: generatedID,
);
}
static Code fromAccountAndSecret(
Type type,
String account,
String issuer,
String secret,
int digits,
) {
final digits =
issuer.toLowerCase() == "steam" ? steamDigits : defaultDigits;
return Code(
account,
issuer,
@ -77,23 +79,21 @@ class Code {
defaultPeriod,
secret,
Algorithm.sha1,
Type.totp,
type,
0,
"otpauth://totp/$issuer:$account?algorithm=SHA1&digits=6&issuer=$issuer&period=30&secret=$secret",
"otpauth://${type.name}/$issuer:$account?algorithm=SHA1${type == Type.steam ? "" : "&digits=$digits"}&issuer=$issuer&period=30&secret=$secret",
);
}
static Code fromRawData(String rawData) {
Uri uri = Uri.parse(rawData);
final issuer = _getIssuer(uri);
final digits =
issuer.toLowerCase() == "steam" ? steamDigits : _getDigits(uri);
try {
return Code(
_getAccount(uri),
issuer,
digits,
_getDigits(uri, issuer),
_getPeriod(uri),
getSanitizedSecret(uri.queryParameters['secret']!),
_getAlgorithm(uri),
@ -147,8 +147,9 @@ class Code {
}
}
static int _getDigits(Uri uri) {
static int _getDigits(Uri uri, String issuer) {
try {
if (issuer.toLowerCase() == "steam") return steamDigits;
return int.parse(uri.queryParameters['digits']!);
} catch (e) {
return defaultDigits;
@ -191,8 +192,10 @@ class Code {
}
static Type _getType(Uri uri) {
if (uri.host == "totp" || uri.host == "steam") {
if (uri.host == "totp") {
return Type.totp;
} else if (uri.host == "steam") {
return Type.steam;
} else if (uri.host == "hotp") {
return Type.hotp;
}
@ -230,6 +233,9 @@ class Code {
enum Type {
totp,
hotp,
steam;
bool get isTOTPCompatible => this == totp || this == steam;
}
enum Algorithm {

View file

@ -20,6 +20,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
late TextEditingController _issuerController;
late TextEditingController _accountController;
late TextEditingController _secretController;
late TextEditingController _digitsController;
late bool _secretKeyObscured;
@override
@ -34,6 +35,9 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
_secretController = TextEditingController(
text: widget.code?.secret,
);
_digitsController = TextEditingController(
text: widget.code?.digits.toString(),
);
_secretKeyObscured = widget.code != null;
super.initState();
}
@ -61,6 +65,8 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
},
decoration: InputDecoration(
hintText: l10n.codeIssuerHint,
floatingLabelBehavior: FloatingLabelBehavior.auto,
labelText: l10n.codeIssuerHint,
),
controller: _issuerController,
autofocus: true,
@ -78,6 +84,8 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
},
decoration: InputDecoration(
hintText: l10n.codeSecretKeyHint,
floatingLabelBehavior: FloatingLabelBehavior.auto,
labelText: l10n.codeSecretKeyHint,
suffixIcon: IconButton(
onPressed: () {
setState(() {
@ -105,9 +113,33 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
},
decoration: InputDecoration(
hintText: l10n.codeAccountHint,
floatingLabelBehavior: FloatingLabelBehavior.auto,
labelText: l10n.codeAccountHint,
),
controller: _accountController,
),
const SizedBox(
height: 20,
),
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return "Please enter some number";
}
if (int.tryParse(value) == null) {
return "Please enter a valid number";
}
return null;
},
readOnly: widget.code?.type == Type.steam,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: l10n.digitsAccountHint,
labelText: l10n.digitsAccountHint,
floatingLabelBehavior: FloatingLabelBehavior.auto,
),
controller: _digitsController,
),
const SizedBox(
height: 40,
),
@ -152,6 +184,10 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
final account = _accountController.text.trim();
final issuer = _issuerController.text.trim();
final secret = _secretController.text.trim().replaceAll(' ', '');
final digits = int.tryParse(_digitsController.text.trim()) ??
(widget.code?.type == Type.steam
? Code.steamDigits
: Code.defaultDigits);
if (widget.code != null && widget.code!.secret != secret) {
ButtonResult? result = await showChoiceActionSheet(
context,
@ -168,14 +204,17 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
}
final Code newCode = widget.code == null
? Code.fromAccountAndSecret(
Type.totp,
account,
issuer,
secret,
digits,
)
: widget.code!.copyWith(
account: account,
issuer: issuer,
secret: secret,
digits: digits,
);
// Verify the validity of the code
getOTP(newCode);

View file

@ -53,7 +53,7 @@ class _CodeWidgetState extends State<CodeWidget> {
String newCode = _getCurrentOTP();
if (newCode != _currentCode.value) {
_currentCode.value = newCode;
if (widget.code.type == Type.totp) {
if (widget.code.type.isTOTPCompatible) {
_nextCode.value = _getNextTotp();
}
}
@ -78,7 +78,7 @@ class _CodeWidgetState extends State<CodeWidget> {
_shouldShowLargeIcon = PreferenceService.instance.shouldShowLargeIcons();
if (!_isInitialized) {
_currentCode.value = _getCurrentOTP();
if (widget.code.type == Type.totp) {
if (widget.code.type.isTOTPCompatible) {
_nextCode.value = _getNextTotp();
}
_isInitialized = true;
@ -213,7 +213,7 @@ class _CodeWidgetState extends State<CodeWidget> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (widget.code.type == Type.totp)
if (widget.code.type.isTOTPCompatible)
CodeTimerProgress(
period: widget.code.period,
),
@ -263,7 +263,7 @@ class _CodeWidgetState extends State<CodeWidget> {
},
),
),
widget.code.type == Type.totp
widget.code.type.isTOTPCompatible
? GestureDetector(
onTap: () {
_copyNextToClipboard();
@ -481,7 +481,7 @@ class _CodeWidgetState extends State<CodeWidget> {
String _getNextTotp() {
try {
assert(widget.code.type == Type.totp);
assert(widget.code.type.isTOTPCompatible);
return getNextTotp(widget.code);
} catch (e) {
return context.l10n.error;

View file

@ -92,9 +92,11 @@ Future<int?> _processBitwardenExportFile(
var account = item['login']['username'];
code = Code.fromAccountAndSecret(
Type.totp,
account,
issuer,
totp,
Code.defaultDigits,
);
}