fix: add digits to setup page and create stream type
This commit is contained in:
parent
b52133fe57
commit
b374dc0dd0
5 changed files with 63 additions and 15 deletions
|
@ -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": {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -92,9 +92,11 @@ Future<int?> _processBitwardenExportFile(
|
|||
var account = item['login']['username'];
|
||||
|
||||
code = Code.fromAccountAndSecret(
|
||||
Type.totp,
|
||||
account,
|
||||
issuer,
|
||||
totp,
|
||||
Code.defaultDigits,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue