mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-14 14:02:49 +00:00
Show a clearer error message when encountering phonefactor:// URI's
Sometimes users get confused when they're trying to scan a Microsoft Authenticator QR code and think the error occurs because of a bug in Aegis.
This commit is contained in:
parent
ddbe9ccfb7
commit
0a6c89cd9d
4 changed files with 40 additions and 22 deletions
|
@ -65,7 +65,7 @@ public class GoogleAuthInfo implements Serializable {
|
||||||
public static GoogleAuthInfo parseUri(String s) throws GoogleAuthInfoException {
|
public static GoogleAuthInfo parseUri(String s) throws GoogleAuthInfoException {
|
||||||
Uri uri = Uri.parse(s);
|
Uri uri = Uri.parse(s);
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
throw new GoogleAuthInfoException(String.format("Bad URI format: %s", s));
|
throw new GoogleAuthInfoException(uri, String.format("Bad URI format: %s", s));
|
||||||
}
|
}
|
||||||
return GoogleAuthInfo.parseUri(uri);
|
return GoogleAuthInfo.parseUri(uri);
|
||||||
}
|
}
|
||||||
|
@ -73,27 +73,27 @@ public class GoogleAuthInfo implements Serializable {
|
||||||
public static GoogleAuthInfo parseUri(Uri uri) throws GoogleAuthInfoException {
|
public static GoogleAuthInfo parseUri(Uri uri) throws GoogleAuthInfoException {
|
||||||
String scheme = uri.getScheme();
|
String scheme = uri.getScheme();
|
||||||
if (scheme == null || !scheme.equals(SCHEME)) {
|
if (scheme == null || !scheme.equals(SCHEME)) {
|
||||||
throw new GoogleAuthInfoException("Unsupported protocol");
|
throw new GoogleAuthInfoException(uri, String.format("Unsupported protocol: %s", scheme));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'secret' is a required parameter
|
// 'secret' is a required parameter
|
||||||
String encodedSecret = uri.getQueryParameter("secret");
|
String encodedSecret = uri.getQueryParameter("secret");
|
||||||
if (encodedSecret == null) {
|
if (encodedSecret == null) {
|
||||||
throw new GoogleAuthInfoException("Parameter 'secret' is not present");
|
throw new GoogleAuthInfoException(uri, "Parameter 'secret' is not present");
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] secret;
|
byte[] secret;
|
||||||
try {
|
try {
|
||||||
secret = parseSecret(encodedSecret);
|
secret = parseSecret(encodedSecret);
|
||||||
} catch (EncodingException e) {
|
} catch (EncodingException e) {
|
||||||
throw new GoogleAuthInfoException("Bad secret", e);
|
throw new GoogleAuthInfoException(uri, "Bad secret", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
OtpInfo info;
|
OtpInfo info;
|
||||||
try {
|
try {
|
||||||
String type = uri.getHost();
|
String type = uri.getHost();
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
throw new GoogleAuthInfoException(String.format("Host not present in URI: %s", uri.toString()));
|
throw new GoogleAuthInfoException(uri, String.format("Host not present in URI: %s", uri.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -117,16 +117,16 @@ public class GoogleAuthInfo implements Serializable {
|
||||||
HotpInfo hotpInfo = new HotpInfo(secret);
|
HotpInfo hotpInfo = new HotpInfo(secret);
|
||||||
String counter = uri.getQueryParameter("counter");
|
String counter = uri.getQueryParameter("counter");
|
||||||
if (counter == null) {
|
if (counter == null) {
|
||||||
throw new GoogleAuthInfoException("Parameter 'counter' is not present");
|
throw new GoogleAuthInfoException(uri, "Parameter 'counter' is not present");
|
||||||
}
|
}
|
||||||
hotpInfo.setCounter(Long.parseLong(counter));
|
hotpInfo.setCounter(Long.parseLong(counter));
|
||||||
info = hotpInfo;
|
info = hotpInfo;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new GoogleAuthInfoException(String.format("Unsupported OTP type: %s", type));
|
throw new GoogleAuthInfoException(uri, String.format("Unsupported OTP type: %s", type));
|
||||||
}
|
}
|
||||||
} catch (OtpInfoException | NumberFormatException e) {
|
} catch (OtpInfoException | NumberFormatException e) {
|
||||||
throw new GoogleAuthInfoException(e);
|
throw new GoogleAuthInfoException(uri, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// provider info used to disambiguate accounts
|
// provider info used to disambiguate accounts
|
||||||
|
@ -166,7 +166,7 @@ public class GoogleAuthInfo implements Serializable {
|
||||||
info.setDigits(Integer.parseInt(digits));
|
info.setDigits(Integer.parseInt(digits));
|
||||||
}
|
}
|
||||||
} catch (OtpInfoException | NumberFormatException e) {
|
} catch (OtpInfoException | NumberFormatException e) {
|
||||||
throw new GoogleAuthInfoException(e);
|
throw new GoogleAuthInfoException(uri, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new GoogleAuthInfo(info, accountName, issuer);
|
return new GoogleAuthInfo(info, accountName, issuer);
|
||||||
|
@ -183,7 +183,7 @@ public class GoogleAuthInfo implements Serializable {
|
||||||
public static Export parseExportUri(String s) throws GoogleAuthInfoException {
|
public static Export parseExportUri(String s) throws GoogleAuthInfoException {
|
||||||
Uri uri = Uri.parse(s);
|
Uri uri = Uri.parse(s);
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
throw new GoogleAuthInfoException("Bad URI format");
|
throw new GoogleAuthInfoException(uri, "Bad URI format");
|
||||||
}
|
}
|
||||||
return GoogleAuthInfo.parseExportUri(uri);
|
return GoogleAuthInfo.parseExportUri(uri);
|
||||||
}
|
}
|
||||||
|
@ -191,17 +191,17 @@ public class GoogleAuthInfo implements Serializable {
|
||||||
public static Export parseExportUri(Uri uri) throws GoogleAuthInfoException {
|
public static Export parseExportUri(Uri uri) throws GoogleAuthInfoException {
|
||||||
String scheme = uri.getScheme();
|
String scheme = uri.getScheme();
|
||||||
if (scheme == null || !scheme.equals(SCHEME_EXPORT)) {
|
if (scheme == null || !scheme.equals(SCHEME_EXPORT)) {
|
||||||
throw new GoogleAuthInfoException("Unsupported protocol");
|
throw new GoogleAuthInfoException(uri, "Unsupported protocol");
|
||||||
}
|
}
|
||||||
|
|
||||||
String host = uri.getHost();
|
String host = uri.getHost();
|
||||||
if (host == null || !host.equals("offline")) {
|
if (host == null || !host.equals("offline")) {
|
||||||
throw new GoogleAuthInfoException("Unsupported host");
|
throw new GoogleAuthInfoException(uri, "Unsupported host");
|
||||||
}
|
}
|
||||||
|
|
||||||
String data = uri.getQueryParameter("data");
|
String data = uri.getQueryParameter("data");
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
throw new GoogleAuthInfoException("Parameter 'data' is not set");
|
throw new GoogleAuthInfoException(uri, "Parameter 'data' is not set");
|
||||||
}
|
}
|
||||||
|
|
||||||
GoogleAuthProtos.MigrationPayload payload;
|
GoogleAuthProtos.MigrationPayload payload;
|
||||||
|
@ -209,7 +209,7 @@ public class GoogleAuthInfo implements Serializable {
|
||||||
byte[] bytes = Base64.decode(data);
|
byte[] bytes = Base64.decode(data);
|
||||||
payload = GoogleAuthProtos.MigrationPayload.parseFrom(bytes);
|
payload = GoogleAuthProtos.MigrationPayload.parseFrom(bytes);
|
||||||
} catch (EncodingException | InvalidProtocolBufferException e) {
|
} catch (EncodingException | InvalidProtocolBufferException e) {
|
||||||
throw new GoogleAuthInfoException(e);
|
throw new GoogleAuthInfoException(uri, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<GoogleAuthInfo> infos = new ArrayList<>();
|
List<GoogleAuthInfo> infos = new ArrayList<>();
|
||||||
|
@ -227,7 +227,7 @@ public class GoogleAuthInfo implements Serializable {
|
||||||
digits = 8;
|
digits = 8;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new GoogleAuthInfoException(String.format("Unsupported digits: %d", params.getDigits().ordinal()));
|
throw new GoogleAuthInfoException(uri, String.format("Unsupported digits: %d", params.getDigits().ordinal()));
|
||||||
}
|
}
|
||||||
|
|
||||||
String algo;
|
String algo;
|
||||||
|
@ -244,7 +244,7 @@ public class GoogleAuthInfo implements Serializable {
|
||||||
algo = "SHA512";
|
algo = "SHA512";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new GoogleAuthInfoException(String.format("Unsupported hash algorithm: %d", params.getAlgorithm().ordinal()));
|
throw new GoogleAuthInfoException(uri, String.format("Unsupported hash algorithm: %d", params.getAlgorithm().ordinal()));
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] secret = params.getSecret().toByteArray();
|
byte[] secret = params.getSecret().toByteArray();
|
||||||
|
@ -258,10 +258,10 @@ public class GoogleAuthInfo implements Serializable {
|
||||||
otp = new HotpInfo(secret, algo, digits, params.getCounter());
|
otp = new HotpInfo(secret, algo, digits, params.getCounter());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new GoogleAuthInfoException(String.format("Unsupported algorithm: %d", params.getType().ordinal()));
|
throw new GoogleAuthInfoException(uri, String.format("Unsupported algorithm: %d", params.getType().ordinal()));
|
||||||
}
|
}
|
||||||
} catch (OtpInfoException e){
|
} catch (OtpInfoException e){
|
||||||
throw new GoogleAuthInfoException(e);
|
throw new GoogleAuthInfoException(uri, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = params.getName();
|
String name = params.getName();
|
||||||
|
|
|
@ -1,16 +1,30 @@
|
||||||
package com.beemdevelopment.aegis.otp;
|
package com.beemdevelopment.aegis.otp;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
public class GoogleAuthInfoException extends Exception {
|
public class GoogleAuthInfoException extends Exception {
|
||||||
public GoogleAuthInfoException(Throwable cause) {
|
private final Uri _uri;
|
||||||
|
|
||||||
|
public GoogleAuthInfoException(Uri uri, Throwable cause) {
|
||||||
super(cause);
|
super(cause);
|
||||||
|
_uri = uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GoogleAuthInfoException(String message) {
|
public GoogleAuthInfoException(Uri uri, String message) {
|
||||||
super(message);
|
super(message);
|
||||||
|
_uri = uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GoogleAuthInfoException(String message, Throwable cause) {
|
public GoogleAuthInfoException(Uri uri, String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
|
_uri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports whether the scheme of the URI is phonefactor://.
|
||||||
|
*/
|
||||||
|
public boolean isPhoneFactor() {
|
||||||
|
return _uri != null && _uri.getScheme() != null && _uri.getScheme().equals("phonefactor");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -19,6 +33,7 @@ public class GoogleAuthInfoException extends Exception {
|
||||||
if (cause == null) {
|
if (cause == null) {
|
||||||
return super.getMessage();
|
return super.getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
return String.format("%s (%s)", super.getMessage(), cause.getMessage());
|
return String.format("%s (%s)", super.getMessage(), cause.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,9 @@ public class ScannerActivity extends AegisActivity implements QrCodeAnalyzer.Lis
|
||||||
}
|
}
|
||||||
} catch (GoogleAuthInfoException e) {
|
} catch (GoogleAuthInfoException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Dialogs.showErrorDialog(this, R.string.read_qr_error, e, ((dialog, which) -> bindPreview(_cameraProvider)));
|
Dialogs.showErrorDialog(this,
|
||||||
|
e.isPhoneFactor() ? R.string.read_qr_error_phonefactor : R.string.read_qr_error,
|
||||||
|
e, ((dialog, which) -> bindPreview(_cameraProvider)));
|
||||||
_cameraProvider.unbindAll();
|
_cameraProvider.unbindAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,6 +192,7 @@
|
||||||
<string name="encryption_enable_biometrics_error">An error occurred while trying to enable biometric unlock. Some devices have poor implementations of biometric authentication and it is likely that yours is one of them. Consider switching to a password-only configuration instead.</string>
|
<string name="encryption_enable_biometrics_error">An error occurred while trying to enable biometric unlock. Some devices have poor implementations of biometric authentication and it is likely that yours is one of them. Consider switching to a password-only configuration instead.</string>
|
||||||
<string name="no_cameras_available">No cameras available</string>
|
<string name="no_cameras_available">No cameras available</string>
|
||||||
<string name="read_qr_error">An error occurred while trying to read the QR code</string>
|
<string name="read_qr_error">An error occurred while trying to read the QR code</string>
|
||||||
|
<string name="read_qr_error_phonefactor">Aegis is not compatible with Microsoft\'s proprietary 2FA algorithm. Please make sure to select \"Setup application without notifications\" when configuring 2FA in Office 365.</string>
|
||||||
<string name="authentication_method_raw">Raw</string>
|
<string name="authentication_method_raw">Raw</string>
|
||||||
<string name="unlocking_vault">Unlocking the vault</string>
|
<string name="unlocking_vault">Unlocking the vault</string>
|
||||||
<string name="unlocking_vault_repair">Unlocking the vault (repairing)</string>
|
<string name="unlocking_vault_repair">Unlocking the vault (repairing)</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue