[FIX] Handle Steam host (#1539)

## Description

It is same as Totp with 5 digits.
Related: https://github.com/ente-io/ente/discussions/1038
This commit is contained in:
Neeraj Gupta 2024-05-03 14:39:40 +05:30 committed by GitHub
commit dc1d2c5b7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 43 additions and 19 deletions

View file

@ -20,7 +20,6 @@
"codeIssuerHint": "Issuer",
"codeSecretKeyHint": "Secret Key",
"codeAccountHint": "Account (you@domain.com)",
"accountKeyType": "Type of key",
"sessionExpired": "Session expired",
"@sessionExpired": {
"description": "Title of the dialog when the users current session is invalid/expired"

View file

@ -37,6 +37,7 @@ import 'package:window_manager/window_manager.dart';
final _logger = Logger("main");
Future<void> initSystemTray() async {
if (PlatformUtil.isMobile()) return;
String path = Platform.isWindows
? 'assets/icons/auth-icon.ico'
: 'assets/icons/auth-icon.png';

View file

@ -2,6 +2,7 @@ import 'package:ente_auth/utils/totp_util.dart';
class Code {
static const defaultDigits = 6;
static const steamDigits = 5;
static const defaultPeriod = 30;
int? generatedID;
@ -57,36 +58,42 @@ 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}"
"&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,
) {
return Code(
account,
issuer,
defaultDigits,
digits,
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&digits=$digits&issuer=$issuer&period=30&secret=$secret",
);
}
static Code fromRawData(String rawData) {
Uri uri = Uri.parse(rawData);
final issuer = _getIssuer(uri);
try {
return Code(
_getAccount(uri),
_getIssuer(uri),
_getDigits(uri),
issuer,
_getDigits(uri, issuer),
_getPeriod(uri),
getSanitizedSecret(uri.queryParameters['secret']!),
_getAlgorithm(uri),
@ -140,10 +147,13 @@ class Code {
}
}
static int _getDigits(Uri uri) {
static int _getDigits(Uri uri, String issuer) {
try {
return int.parse(uri.queryParameters['digits']!);
} catch (e) {
if (issuer.toLowerCase() == "steam") {
return steamDigits;
}
return defaultDigits;
}
}
@ -186,6 +196,8 @@ class Code {
static Type _getType(Uri uri) {
if (uri.host == "totp") {
return Type.totp;
} else if (uri.host == "steam") {
return Type.steam;
} else if (uri.host == "hotp") {
return Type.hotp;
}
@ -223,6 +235,9 @@ class Code {
enum Type {
totp,
hotp,
steam;
bool get isTOTPCompatible => this == totp || this == steam;
}
enum Algorithm {

View file

@ -61,6 +61,8 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
},
decoration: InputDecoration(
hintText: l10n.codeIssuerHint,
floatingLabelBehavior: FloatingLabelBehavior.auto,
labelText: l10n.codeIssuerHint,
),
controller: _issuerController,
autofocus: true,
@ -78,6 +80,8 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
},
decoration: InputDecoration(
hintText: l10n.codeSecretKeyHint,
floatingLabelBehavior: FloatingLabelBehavior.auto,
labelText: l10n.codeSecretKeyHint,
suffixIcon: IconButton(
onPressed: () {
setState(() {
@ -105,12 +109,12 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
},
decoration: InputDecoration(
hintText: l10n.codeAccountHint,
floatingLabelBehavior: FloatingLabelBehavior.auto,
labelText: l10n.codeAccountHint,
),
controller: _accountController,
),
const SizedBox(
height: 40,
),
const SizedBox(height: 40),
SizedBox(
width: 400,
child: OutlinedButton(
@ -152,6 +156,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
final account = _accountController.text.trim();
final issuer = _issuerController.text.trim();
final secret = _secretController.text.trim().replaceAll(' ', '');
final isStreamCode = issuer.toLowerCase() == "steam";
if (widget.code != null && widget.code!.secret != secret) {
ButtonResult? result = await showChoiceActionSheet(
context,
@ -168,9 +173,11 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
}
final Code newCode = widget.code == null
? Code.fromAccountAndSecret(
isStreamCode ? Type.steam : Type.totp,
account,
issuer,
secret,
isStreamCode ? Code.steamDigits : Code.defaultDigits,
)
: widget.code!.copyWith(
account: account,

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,
);
}

View file

@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart';
import 'package:otp/otp.dart' as otp;
String getOTP(Code code) {
if(code.type == Type.hotp) {
if (code.type == Type.hotp) {
return _getHOTPCode(code);
}
return otp.OTP.generateTOTPCodeString(
@ -60,4 +60,4 @@ String safeDecode(String value) {
debugPrint("Failed to decode $e");
return value;
}
}
}

View file

@ -1,6 +1,6 @@
name: ente_auth
description: ente two-factor authenticator
version: 2.0.56+256
version: 2.0.57+257
publish_to: none
environment: