Integrar a criação de chaves de acesso com toque único e login com solicitações biométricas

No Android 15, o Credential Manager oferece suporte a um fluxo de toque único para criação e recuperação de credenciais. Nesse fluxo, as informações da credencial que está sendo criada ou usada são mostradas diretamente no prompt biométrico, junto com um ponto de entrada para mais opções. Esse processo simplificado cria um processo de criação e recuperação de credenciais mais eficiente e simplificado.

Requisitos :

  • A biometria foi configurada no dispositivo do usuário, e ele permite o uso dela para autenticação em aplicativos.
  • Para fluxos de login, esse recurso está ativado apenas para cenários de conta única, mesmo que haja várias credenciais (como chave de acesso e senha) disponíveis para essa conta.

Ativar o toque único em fluxos de criação de chaves de acesso

As etapas de criação desse método correspondem ao processo de criação de credenciais atual. Na BeginCreatePublicKeyCredentialRequest, use handleCreatePasskeyQuery() para processar a solicitação se ela for para uma chave de acesso.

is BeginCreatePublicKeyCredentialRequest -> {
    Log.i(TAG, "Request is passkey type")
    return handleCreatePasskeyQuery(request, passwordCount, passkeyCount)
}

Em seu handleCreatePasskeyQuery(), inclua BiometricPromptData com a classe CreateEntry:

val createEntry = CreateEntry(
    // Additional properties...
    biometricPromptData = BiometricPromptData(
        allowedAuthenticators = allowedAuthenticator
    ),
)

Os provedores de credenciais precisam definir explicitamente a propriedade allowedAuthenticator na instância BiometricPromptData. Se essa propriedade não estiver definida, o valor padrão será DEVICE_WEAK. Defina a propriedade cryptoObject opcional, se necessário, para seu caso de uso.

Ativar o toque único em fluxos de chaves de acesso de login

Semelhante ao fluxo de criação de chaves de acesso, isso vai seguir a configuração atual para processar o login do usuário. Em BeginGetPublicKeyCredentialOption, use populatePasskeyData() para coletar as informações relevantes sobre a solicitação de autenticação:

is BeginGetPublicKeyCredentialOption -> {
    // ... other logic

    populatePasskeyData(
        origin,
        option,
        responseBuilder,
        autoSelectEnabled,
        allowedAuthenticator
    )

    // ... other logic as needed
}

Semelhante a CreateEntry, uma instância BiometricPromptData é definida para a instância PublicKeyCredentialEntry. Se não estiver definido explicitamente, allowedAuthenticator será definido como BIOMETRIC_WEAK.

PublicKeyCredentialEntry(
    // other properties...

    biometricPromptData = BiometricPromptData(
        allowedAuthenticators = allowedAuthenticator
    )
)

Processar a seleção de entrada de credenciais

Ao processar a seleção de entrada de credenciais para criação de chave de acesso ou seleção de chave de acesso durante o login, chame PendingIntentHandler's retrieveProviderCreateCredentialRequest ou retrieveProviderGetCredentialRequest, conforme apropriado. Esses objetos de retorno contêm os metadados necessários para o provedor. Por exemplo, ao processar a seleção de entrada de criação de chaves de acesso, atualize o código conforme mostrado:

val createRequest = PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
if (createRequest == null) {
    Log.i(TAG, "request is null")
    setUpFailureResponseAndFinish("Unable to extract request from intent")
    return
}
// Other logic...

val biometricPromptResult = createRequest.biometricPromptResult

// Add your logic based on what needs to be done
// after getting biometrics

if (createRequest.callingRequest is CreatePublicKeyCredentialRequest) {
    val publicKeyRequest: CreatePublicKeyCredentialRequest =
        createRequest.callingRequest as CreatePublicKeyCredentialRequest

    if (biometricPromptResult == null) {
        // Do your own authentication flow, if needed
    } else if (biometricPromptResult.isSuccessful) {
        createPasskey(
            publicKeyRequest.requestJson,
            createRequest.callingAppInfo,
            publicKeyRequest.clientDataHash,
            accountId
        )
    } else {
        val error = biometricPromptResult.authenticationError
        // Process the error
    }

    // Other logic...
}

Este exemplo contém informações sobre o sucesso do fluxo biométrico. Ele também contém outras informações sobre a credencial. Se o fluxo falhar, use o código de erro em biometricPromptResult.authenticationError para tomar decisões. Os códigos de erro retornados como parte de biometricPromptResult.authenticationError.errorCode são os mesmos códigos de erro definidos na biblioteca androidx.biometric, como androidx.biometric.BiometricPrompt.NO_SPACE, androidx.biometric.BiometricPrompt.UNABLE_TO_PROCESS, androidx.biometric.BiometricPrompt.ERROR_TIMEOUT e semelhantes. O authenticationError também vai conter uma mensagem de erro associada ao errorCode que pode ser mostrada em uma interface.

Da mesma forma, extraia metadados durante a retrieveProviderGetCredentialRequest. Verifique se o fluxo biométrico é null. Em caso afirmativo, configure sua própria biometria para autenticar. Isso é semelhante à forma como a operação get é instrumentada:

val getRequest =
    PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)

if (getRequest == null) {
    Log.i(TAG, "request is null")
    setUpFailureResponseAndFinish("Unable to extract request from intent")
    return
}

// Other logic...

val biometricPromptResult = getRequest.biometricPromptResult

// Add your logic based on what needs to be done
// after getting biometrics

if (biometricPromptResult == null) {
    // Do your own authentication flow, if necessary
} else if (biometricPromptResult.isSuccessful) {

    Log.i(TAG, "The response from the biometricPromptResult was ${biometricPromptResult.authenticationResult?.authenticationType}")

    validatePasskey(
        publicKeyRequest.requestJson,
        origin,
        packageName,
        uid,
        passkey.username,
        credId,
        privateKey
    )
} else {
    val error = biometricPromptResult.authenticationError
    // Process the error
}

// Other logic...