diff --git a/javasample/src/main/java/com/oursky/authgeartest/MainViewModel.java b/javasample/src/main/java/com/oursky/authgeartest/MainViewModel.java index c923d032..65cd8b03 100644 --- a/javasample/src/main/java/com/oursky/authgeartest/MainViewModel.java +++ b/javasample/src/main/java/com/oursky/authgeartest/MainViewModel.java @@ -18,6 +18,7 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import com.oursky.authgear.BiometricAuthenticator; import com.oursky.authgear.PreAuthenticatedURLNotAllowedException; import com.oursky.authgear.PreAuthenticatedURLOptions; import com.oursky.authgear.Authgear; @@ -68,10 +69,12 @@ import com.tencent.mm.opensdk.openapi.WXAPIFactory; import java.util.Date; +import java.util.List; @SuppressWarnings("ConstantConditions") public class MainViewModel extends AndroidViewModel implements AuthgearDelegate { - private static final int ALLOWED = BiometricManager.Authenticators.BIOMETRIC_STRONG; + private static final List ALLOWED_AUTHENTICATORS_ON_ENABLE = List.of(BiometricAuthenticator.BIOMETRIC_STRONG); + private static final List ALLOWED_AUTHENTICATORS_ON_AUTHENTICATE = List.of(BiometricAuthenticator.BIOMETRIC_STRONG, BiometricAuthenticator.DEVICE_CREDENTIAL); private static final String TAG = MainViewModel.class.getSimpleName(); private Authgear mAuthgear = null; private IWXAPI wechatAPI; @@ -136,7 +139,7 @@ private void updateBiometricState() { if (Build.VERSION.SDK_INT >= 23) { mAuthgear.checkBiometricSupported( this.getApplication(), - ALLOWED + ALLOWED_AUTHENTICATORS_ON_ENABLE ); supported = true; } @@ -609,7 +612,8 @@ private BiometricOptions makeBiometricOptions(FragmentActivity activity) { "Biometric authentication", "Use biometric to authenticate", "Cancel", - ALLOWED, + ALLOWED_AUTHENTICATORS_ON_ENABLE, + ALLOWED_AUTHENTICATORS_ON_AUTHENTICATE, true ); } @@ -634,7 +638,7 @@ public void enableBiometric(FragmentActivity activity) { mIsLoading.setValue(true); try { - mAuthgear.checkBiometricSupported(activity, ALLOWED); + mAuthgear.checkBiometricSupported(activity, ALLOWED_AUTHENTICATORS_ON_ENABLE); } catch (Exception e) { mIsLoading.setValue(false); setError(e); diff --git a/sdk/src/main/java/com/oursky/authgear/Authgear.kt b/sdk/src/main/java/com/oursky/authgear/Authgear.kt index 7c3e6825..2fc6cece 100644 --- a/sdk/src/main/java/com/oursky/authgear/Authgear.kt +++ b/sdk/src/main/java/com/oursky/authgear/Authgear.kt @@ -640,8 +640,8 @@ constructor( */ @MainThread @RequiresApi(api = Build.VERSION_CODES.M) - fun checkBiometricSupported(context: Context, allowedAuthenticators: Int) { - core.checkBiometricSupported(context, allowedAuthenticators) + fun checkBiometricSupported(context: Context, allowedAuthenticatorsOnEnable: List) { + core.checkBiometricSupported(context, allowedAuthenticatorsOnEnable) } /** diff --git a/sdk/src/main/java/com/oursky/authgear/AuthgearCore.kt b/sdk/src/main/java/com/oursky/authgear/AuthgearCore.kt index ea87c78f..33c7cf23 100644 --- a/sdk/src/main/java/com/oursky/authgear/AuthgearCore.kt +++ b/sdk/src/main/java/com/oursky/authgear/AuthgearCore.kt @@ -886,13 +886,11 @@ internal class AuthgearCore( } @RequiresApi(api = Build.VERSION_CODES.M) - fun checkBiometricSupported(context: Context, allowed: Int) { + fun checkBiometricSupported(context: Context, allowedAuthenticatorsOnEnable: List) { requireIsInitialized() requireMinimumBiometricAPILevel() - var allowed = allowed - ensureAllowedIsValid(allowed) - allowed = convertAllowed(allowed) + val allowed = convertAllowed(allowedAuthenticatorsOnEnable) val result = BiometricManager.from(context).canAuthenticate(allowed) if (result != BiometricManager.BIOMETRIC_SUCCESS) { throw wrapException(BiometricCanAuthenticateException(result)) @@ -931,21 +929,22 @@ internal class AuthgearCore( val accessToken: String = this.accessToken ?: throw UnauthenticatedUserException() - ensureAllowedIsValid(options.allowedAuthenticators) - val allowed = convertAllowed(options.allowedAuthenticators) + val allowedAuthenticatorsOnEnable = convertAllowed(options.allowedAuthenticatorsOnEnable) + val allowedAuthenticatorsOnAuthenticate = convertAllowed(options.allowedAuthenticatorsOnAuthenticate) + val promptInfo = buildPromptInfo( options.title, options.subtitle, options.description, options.negativeButtonText, - allowed + allowedAuthenticatorsOnEnable ) val kid = UUID.randomUUID().toString() val alias = "com.authgear.keys.biometric.$kid" val spec = makeGenerateKeyPairSpec( alias, - authenticatorTypesToKeyProperties(allowed), + authenticatorTypesToKeyProperties(allowedAuthenticatorsOnAuthenticate), options.invalidatedByBiometricEnrollment) val challenge = this.oauthRepo.oauthChallenge("biometric_request").token val keyPair = createKeyPair(spec) @@ -1019,14 +1018,13 @@ internal class AuthgearCore( requireIsInitialized() requireMinimumBiometricAPILevel() - ensureAllowedIsValid(options.allowedAuthenticators) - val allowed = convertAllowed(options.allowedAuthenticators) + val allowedAuthenticatorsOnAuthenticate = convertAllowed(options.allowedAuthenticatorsOnAuthenticate) val promptInfo = buildPromptInfo( options.title, options.subtitle, options.description, options.negativeButtonText, - allowed + allowedAuthenticatorsOnAuthenticate ) val challenge = this.oauthRepo.oauthChallenge("biometric_request").token diff --git a/sdk/src/main/java/com/oursky/authgear/Biometric.kt b/sdk/src/main/java/com/oursky/authgear/Biometric.kt index 06722582..0b0dd6a1 100644 --- a/sdk/src/main/java/com/oursky/authgear/Biometric.kt +++ b/sdk/src/main/java/com/oursky/authgear/Biometric.kt @@ -9,10 +9,6 @@ import androidx.biometric.BiometricPrompt import androidx.biometric.BiometricPrompt.PromptInfo import java.security.* -internal const val BIOMETRIC_ONLY = BiometricManager.Authenticators.BIOMETRIC_STRONG -internal const val BIOMETRIC_OR_DEVICE_CREDENTIAL = - BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL - @RequiresApi(api = Build.VERSION_CODES.M) internal fun makeGenerateKeyPairSpec(alias: String, allowed: Int, invalidatedByBiometricEnrollment: Boolean): KeyGenParameterSpec { val builder = KeyGenParameterSpec.Builder( @@ -107,17 +103,25 @@ internal fun authenticatorTypesToKeyProperties(allowed: Int): Int { return out } -internal fun ensureAllowedIsValid(allowed: Int) { - if (allowed != BIOMETRIC_ONLY && allowed != BIOMETRIC_OR_DEVICE_CREDENTIAL) { - throw IllegalArgumentException("AuthenticateTypes must be BIOMETRIC_STRONG or BIOMETRIC_STRONG | DEVICE_CREDENTIAL") +internal fun convertAllowed(allowedAuthenticators: List): Int { + var flags = 0 + for (allowed in allowedAuthenticators) { + flags = flags or allowed.raw } -} -internal fun convertAllowed(allowed: Int): Int { - if (Build.VERSION.SDK_INT < 30 && (allowed and BiometricManager.Authenticators.DEVICE_CREDENTIAL) != 0) { - return allowed xor BiometricManager.Authenticators.DEVICE_CREDENTIAL + // The official documentation says that on 29 and below, + // DEVICE_CREDENTIAL and BIOMETRIC_STRONG | DEVICE_CREDENTIAL + // are unsupported. + // https://developer.android.com/identity/sign-in/biometric-auth#:~:text=The%20following%20combinations%20of%20authenticator%20types%20aren%27t%20supported%20on%20Android%2010%20(API%20level%2029)%20and%20lower + if (Build.VERSION.SDK_INT < 30) { + if (flags == BiometricManager.Authenticators.DEVICE_CREDENTIAL || flags == (BiometricManager.Authenticators.DEVICE_CREDENTIAL or BiometricManager.Authenticators.BIOMETRIC_STRONG)) { + // Fallback to BiometricManager.Authenticators.BIOMETRIC_STRONG + // which is a strong default. + flags = BiometricManager.Authenticators.BIOMETRIC_STRONG + } } - return allowed + + return flags } internal fun canAuthenticateResultToString(result: Int): String { @@ -160,7 +164,6 @@ internal fun buildPromptInfo( negativeButtonText: String, allowed: Int ): PromptInfo { - ensureAllowedIsValid(allowed) val builder = PromptInfo.Builder() .setTitle(title) .setSubtitle(subtitle) diff --git a/sdk/src/main/java/com/oursky/authgear/BiometricAuthenticator.kt b/sdk/src/main/java/com/oursky/authgear/BiometricAuthenticator.kt new file mode 100644 index 00000000..e365e510 --- /dev/null +++ b/sdk/src/main/java/com/oursky/authgear/BiometricAuthenticator.kt @@ -0,0 +1,8 @@ +package com.oursky.authgear + +import androidx.biometric.BiometricManager + +enum class BiometricAuthenticator(val raw: Int) { + BIOMETRIC_STRONG(BiometricManager.Authenticators.BIOMETRIC_STRONG), + DEVICE_CREDENTIAL(BiometricManager.Authenticators.DEVICE_CREDENTIAL) +} \ No newline at end of file diff --git a/sdk/src/main/java/com/oursky/authgear/BiometricOptions.kt b/sdk/src/main/java/com/oursky/authgear/BiometricOptions.kt index af04a20a..2d33c228 100644 --- a/sdk/src/main/java/com/oursky/authgear/BiometricOptions.kt +++ b/sdk/src/main/java/com/oursky/authgear/BiometricOptions.kt @@ -8,6 +8,7 @@ data class BiometricOptions constructor( var subtitle: String, var description: String, var negativeButtonText: String, - var allowedAuthenticators: Int, + var allowedAuthenticatorsOnEnable: List, + var allowedAuthenticatorsOnAuthenticate: List, var invalidatedByBiometricEnrollment: Boolean ) \ No newline at end of file