fix: User Verified flag during registration #2283

This commit is contained in:
J-Jamet
2025-11-26 10:16:51 +01:00
parent f9051ce787
commit 609b536898
4 changed files with 32 additions and 10 deletions

View File

@@ -343,14 +343,17 @@ class PasskeyProviderService : CredentialProviderService() {
private fun MutableList<CreateEntry>.addPendingIntentCreationNewEntry( private fun MutableList<CreateEntry>.addPendingIntentCreationNewEntry(
accountName: String, accountName: String,
searchInfo: SearchInfo? searchInfo: SearchInfo?,
userVerification: UserVerificationRequirement
) { ) {
Log.d(TAG, "Add pending intent for registration in opened database to create new item") Log.d(TAG, "Add pending intent for registration in opened database to create new item")
// TODO add a setting to directly store in a specific group // TODO add a setting to directly store in a specific group
PasskeyLauncherActivity.getPendingIntent( PasskeyLauncherActivity.getPendingIntent(
context = applicationContext, context = applicationContext,
specialMode = SpecialMode.REGISTRATION, specialMode = SpecialMode.REGISTRATION,
searchInfo = searchInfo searchInfo = searchInfo,
userVerification = userVerification,
userVerifiedWithAuth = false
)?.let { pendingIntent -> )?.let { pendingIntent ->
this.add( this.add(
CreateEntry( CreateEntry(
@@ -379,6 +382,7 @@ class PasskeyProviderService : CredentialProviderService() {
) )
val relyingPartyId = publicKeyCredentialCreationOptions.relyingPartyEntity.id val relyingPartyId = publicKeyCredentialCreationOptions.relyingPartyEntity.id
val searchInfo = buildPasskeySearchInfo(relyingPartyId) val searchInfo = buildPasskeySearchInfo(relyingPartyId)
val userVerification = publicKeyCredentialCreationOptions.authenticatorSelection.userVerification
Log.d(TAG, "Build passkey search for relying party $relyingPartyId") Log.d(TAG, "Build passkey search for relying party $relyingPartyId")
SearchHelper.checkAutoSearchInfo( SearchHelper.checkAutoSearchInfo(
context = this, context = this,
@@ -389,7 +393,11 @@ class PasskeyProviderService : CredentialProviderService() {
throw RegisterInReadOnlyDatabaseException() throw RegisterInReadOnlyDatabaseException()
} else { } else {
// To create a new entry // To create a new entry
createEntries.addPendingIntentCreationNewEntry(accountName, searchInfo) createEntries.addPendingIntentCreationNewEntry(
accountName = accountName,
searchInfo = searchInfo,
userVerification = userVerification
)
/* TODO Overwrite /* TODO Overwrite
// To select an existing entry and permit an overwrite // To select an existing entry and permit an overwrite
Log.w(TAG, "Passkey already registered") Log.w(TAG, "Passkey already registered")
@@ -420,7 +428,11 @@ class PasskeyProviderService : CredentialProviderService() {
if (database.isReadOnly) { if (database.isReadOnly) {
throw RegisterInReadOnlyDatabaseException() throw RegisterInReadOnlyDatabaseException()
} else { } else {
createEntries.addPendingIntentCreationNewEntry(accountName, searchInfo) createEntries.addPendingIntentCreationNewEntry(
accountName = accountName,
searchInfo = searchInfo,
userVerification = userVerification
)
} }
callback(createEntries) callback(createEntries)
}, },
@@ -429,7 +441,8 @@ class PasskeyProviderService : CredentialProviderService() {
Log.d(TAG, "Add pending intent for passkey registration in closed database") Log.d(TAG, "Add pending intent for passkey registration in closed database")
PasskeyLauncherActivity.getPendingIntent( PasskeyLauncherActivity.getPendingIntent(
context = applicationContext, context = applicationContext,
specialMode = SpecialMode.REGISTRATION specialMode = SpecialMode.REGISTRATION,
userVerifiedWithAuth = true
)?.let { pendingIntent -> )?.let { pendingIntent ->
createEntries.add( createEntries.add(
CreateEntry( CreateEntry(

View File

@@ -148,11 +148,12 @@ data class PublicKeyCredentialDescriptor(
} }
} }
// https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorselectioncriteria
data class AuthenticatorSelectionCriteria( data class AuthenticatorSelectionCriteria(
val authenticatorAttachment: String? = null, val authenticatorAttachment: String? = null,
val residentKey: ResidentKeyRequirement? = null, val residentKey: ResidentKeyRequirement? = null,
val requireResidentKey: Boolean?, val requireResidentKey: Boolean?,
val userVerification: UserVerificationRequirement? = UserVerificationRequirement.PREFERRED val userVerification: UserVerificationRequirement = UserVerificationRequirement.PREFERRED
) { ) {
companion object { companion object {
fun JSONObject.getAuthenticatorSelectionCriteria( fun JSONObject.getAuthenticatorSelectionCriteria(
@@ -166,7 +167,9 @@ data class AuthenticatorSelectionCriteria(
ResidentKeyRequirement.fromString(authenticatorSelection.getString("residentKey")) ResidentKeyRequirement.fromString(authenticatorSelection.getString("residentKey"))
else null else null
val requireResidentKey = authenticatorSelection.optBoolean("requireResidentKey", false) val requireResidentKey = authenticatorSelection.optBoolean("requireResidentKey", false)
val userVerification = UserVerificationRequirement.fromString(authenticatorSelection.optString("userVerification", "preferred")) val userVerification = UserVerificationRequirement
.fromString(authenticatorSelection.optString("userVerification", "preferred"))
?: UserVerificationRequirement.PREFERRED
// https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement // https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement
if (residentKey == null) { if (residentKey == null) {
residentKey = if (requireResidentKey) { residentKey = if (requireResidentKey) {
@@ -195,7 +198,9 @@ enum class ResidentKeyRequirement(val value: String) {
} }
companion object { companion object {
fun fromString(value: String): ResidentKeyRequirement? { fun fromString(value: String): ResidentKeyRequirement? {
return ResidentKeyRequirement.entries.firstOrNull { it.value == value } return ResidentKeyRequirement.entries.firstOrNull {
it.value.equals(other = value, ignoreCase = true)
}
} }
} }
} }
@@ -210,7 +215,9 @@ enum class UserVerificationRequirement(val value: String) {
} }
companion object { companion object {
fun fromString(value: String): UserVerificationRequirement? { fun fromString(value: String): UserVerificationRequirement? {
return UserVerificationRequirement.entries.firstOrNull { it.value == value } return UserVerificationRequirement.entries.firstOrNull {
it.value.equals(other = value, ignoreCase = true)
}
} }
} }
} }

View File

@@ -557,6 +557,7 @@ object PasskeyHelper {
*/ */
fun buildCreatePublicKeyCredentialResponse( fun buildCreatePublicKeyCredentialResponse(
publicKeyCredentialCreationParameters: PublicKeyCredentialCreationParameters, publicKeyCredentialCreationParameters: PublicKeyCredentialCreationParameters,
userVerified: Boolean,
backupEligibility: Boolean, backupEligibility: Boolean,
backupState: Boolean backupState: Boolean
): CreatePublicKeyCredentialResponse { ): CreatePublicKeyCredentialResponse {
@@ -574,7 +575,7 @@ object PasskeyHelper {
keyTypeId = keyTypeId keyTypeId = keyTypeId
) ?: mapOf<Int, Any>()), ) ?: mapOf<Int, Any>()),
userPresent = true, userPresent = true,
userVerified = true, userVerified = userVerified,
backupEligibility = backupEligibility, backupEligibility = backupEligibility,
backupState = backupState, backupState = backupState,
publicKeyTypeId = keyTypeId, publicKeyTypeId = keyTypeId,

View File

@@ -525,6 +525,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
intent = responseIntent, intent = responseIntent,
response = buildCreatePublicKeyCredentialResponse( response = buildCreatePublicKeyCredentialResponse(
publicKeyCredentialCreationParameters = it, publicKeyCredentialCreationParameters = it,
userVerified = mUserVerified,
backupEligibility = passkey?.backupEligibility backupEligibility = passkey?.backupEligibility
?: mBackupEligibility, ?: mBackupEligibility,
backupState = passkey?.backupState backupState = passkey?.backupState