PasskeyCredentialSelectionActivity.kt

4.2 KB · raw

1package com.peachpasswords.passkeys23import android.app.PendingIntent4import android.content.Context5import android.content.Intent6import android.os.Build7import android.os.Bundle8import androidx.activity.ComponentActivity9import androidx.credentials.GetCredentialResponse10import androidx.credentials.GetPublicKeyCredentialOption11import androidx.credentials.PublicKeyCredential12import androidx.credentials.exceptions.GetCredentialUnsupportedException13import androidx.credentials.provider.PendingIntentHandler14import androidx.lifecycle.lifecycleScope15import com.peachpasswords.vault.VaultRepository16import dagger.hilt.android.AndroidEntryPoint17import javax.inject.Inject18import kotlinx.coroutines.launch1920@AndroidEntryPoint21class PasskeyCredentialSelectionActivity : ComponentActivity() {22    @Inject lateinit var vault: VaultRepository2324    override fun onCreate(savedInstanceState: Bundle?) {25        super.onCreate(savedInstanceState)26        lifecycleScope.launch { completeSelection() }27    }2829    private suspend fun completeSelection() {30        val result = Intent()31        val entryId = intent.getStringExtra(EXTRA_ENTRY_ID)32        val providerRequest = runCatching {33            PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)34        }.getOrNull()35        val option = providerRequest?.credentialOptions36            ?.filterIsInstance<GetPublicKeyCredentialOption>()37            ?.firstOrNull()38        val requestJson = option?.requestJson ?: intent.getStringExtra(EXTRA_REQUEST_JSON)39        val entry = entryId?.let(vault::getEntry)40        val origin = providerRequest?.callingAppInfo?.let { info ->41            runCatching { info.getOrigin(info.packageName) }.getOrNull()42        }43        val assertion = if (entry != null && requestJson != null) {44            PasskeyAssertion.build(45                entry = entry,46                requestJson = requestJson,47                origin = origin,48                clientDataHashOverride = option?.clientDataHash,49            )50        } else {51            null52        }5354        if (entry != null && assertion != null) {55            val passkey = entry.passkey56            val persisted = if (passkey != null) {57                val now = System.currentTimeMillis()58                runCatching {59                    vault.updateEntry(60                        entry.copy(61                            passkey = passkey.copy(signCount = assertion.nextSignCount),62                            signCount = assertion.nextSignCount,63                            lastUsedAt = now,64                        ),65                    )66                }.isSuccess67            } else {68                false69            }70            if (persisted) {71                PendingIntentHandler.setGetCredentialResponse(72                    result,73                    GetCredentialResponse(PublicKeyCredential(assertion.responseJson)),74                )75            } else {76                PendingIntentHandler.setGetCredentialException(77                    result,78                    GetCredentialUnsupportedException("Peach could not update this passkey."),79                )80            }81        } else {82            PendingIntentHandler.setGetCredentialException(83                result,84                GetCredentialUnsupportedException("Peach cannot sign this passkey yet."),85            )86        }87        setResult(RESULT_OK, result)88        finish()89    }9091    companion object {92        private const val EXTRA_ENTRY_ID = "com.peachpasswords.passkeys.EXTRA_ENTRY_ID"93        private const val EXTRA_REQUEST_JSON = "com.peachpasswords.passkeys.EXTRA_REQUEST_JSON"9495        fun pendingIntent(context: Context, entryId: String, requestJson: String): PendingIntent {96            val intent = Intent(context, PasskeyCredentialSelectionActivity::class.java)97                .putExtra(EXTRA_ENTRY_ID, entryId)98                .putExtra(EXTRA_REQUEST_JSON, requestJson)99            val mutableFlag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE else 0100            return PendingIntent.getActivity(101                context,102                "$entryId:$requestJson".hashCode(),103                intent,104                PendingIntent.FLAG_UPDATE_CURRENT or mutableFlag,105            )106        }107    }108}