Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<BiometricAuthenticator> ALLOWED_AUTHENTICATORS_ON_ENABLE = List.of(BiometricAuthenticator.BIOMETRIC_STRONG);
private static final List<BiometricAuthenticator> 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;
Expand Down Expand Up @@ -136,7 +139,7 @@ private void updateBiometricState() {
if (Build.VERSION.SDK_INT >= 23) {
mAuthgear.checkBiometricSupported(
this.getApplication(),
ALLOWED
ALLOWED_AUTHENTICATORS_ON_ENABLE
);
supported = true;
}
Expand Down Expand Up @@ -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
);
}
Expand All @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions sdk/src/main/java/com/oursky/authgear/Authgear.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<BiometricAuthenticator>) {
core.checkBiometricSupported(context, allowedAuthenticatorsOnEnable)
}

/**
Expand Down
20 changes: 9 additions & 11 deletions sdk/src/main/java/com/oursky/authgear/AuthgearCore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<BiometricAuthenticator>) {
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))
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
29 changes: 16 additions & 13 deletions sdk/src/main/java/com/oursky/authgear/Biometric.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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<BiometricAuthenticator>): 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 {
Expand Down Expand Up @@ -160,7 +164,6 @@ internal fun buildPromptInfo(
negativeButtonText: String,
allowed: Int
): PromptInfo {
ensureAllowedIsValid(allowed)
val builder = PromptInfo.Builder()
.setTitle(title)
.setSubtitle(subtitle)
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
3 changes: 2 additions & 1 deletion sdk/src/main/java/com/oursky/authgear/BiometricOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ data class BiometricOptions constructor(
var subtitle: String,
var description: String,
var negativeButtonText: String,
var allowedAuthenticators: Int,
var allowedAuthenticatorsOnEnable: List<BiometricAuthenticator>,
var allowedAuthenticatorsOnAuthenticate: List<BiometricAuthenticator>,
var invalidatedByBiometricEnrollment: Boolean
)