Com suporte para chaves de acesso, login federado e provedores de autenticação de terceiros, o Gerenciador de credenciais é a API recomendada para autenticação no Android que oferece um ambiente seguro e conveniente que permite aos usuários sincronizar e gerenciar as próprias credenciais. Para desenvolvedores que usam credenciais do FIDO2 locais, atualize seu app para oferecer suporte à autenticação de chaves de acesso pela integração com a API Credential Manager. Este documento descreve como migrar seu projeto do FIDO2 para o Gerenciador de credenciais.
Motivos para migrar do FIDO2 para o Gerenciador de credenciais
Na maioria dos casos, você precisa migrar o provedor de autenticação do seu app Android para o Gerenciador de credenciais: Os motivos para essa migração incluem:
- Suporte a chaves de acesso: o Gerenciador de credenciais oferece suporte a chaves de acesso, um novo mecanismo de autenticação sem senha que é mais seguro e fácil de usar do que senhas.
- Vários métodos de login: o Gerenciador de credenciais oferece suporte a vários métodos de login, incluindo senhas, chaves de acesso e métodos de login federados. Isso facilita a autenticação dos usuários no seu app, independente do método de autenticação preferido.
- Suporte a provedores de credenciais de terceiros: no Android 14 e versões mais recentes, o Gerenciador de credenciais oferece suporte a vários provedores de credenciais de terceiros. Isso significa que seus usuários podem usar as credenciais atuais de outros provedores para fazer login no seu app.
- Experiência do usuário consistente: o Gerenciador de credenciais oferece uma experiência do usuário mais consistente para autenticação em todos os apps e mecanismos de login. Isso facilita que os usuários entendam e usem o fluxo de autenticação do app.
Para iniciar a migração do FIDO2 para o Gerenciador de credenciais, siga as etapas abaixo.
Atualizar dependências
Atualize o plug-in do Kotlin no build.gradle do projeto para a versão 1.8.10 ou mais recente.
plugins { //… id 'org.jetbrains.kotlin.android' version '1.8.10' apply false //… }
No build.gradle do projeto, atualize as dependências para usar o Gerenciador de credenciais e a autenticação do Google Play Services.
dependencies { // ... // Credential Manager: implementation 'androidx.credentials:credentials:<latest-version>' // Play Services Authentication: // Optional - needed for credentials support from play services, for devices running // Android 13 and below: implementation 'androidx.credentials:credentials-play-services-auth:<latest-version>' // ... }
Substitua a inicialização do FIDO pela inicialização do Gerenciador de credenciais. Adicione essa declaração na classe que você usa para criação de chaves de acesso e métodos de login:
val credMan = CredentialManager.create(context)
Criar chaves de acesso
Você precisará criar uma nova chave de acesso, associá-la à conta de um usuário e armazenar a chave pública no servidor para que o usuário possa fazer login com ela. Configure seu app com esse recurso atualizando as chamadas de função de registro.
Para acessar os parâmetros necessários que são enviados ao método
createCredential()
durante a criação da chave de acesso, adicionename("residentKey").value("required")
(conforme descrito na especificação WebAuthn) à chamada de servidorregisterRequest()
.suspend fun registerRequest(sessionId: String ... { // ... .method("POST", jsonRequestBody { name("attestation").value("none") name("authenticatorSelection").objectValue { name("residentKey").value("required") } }).build() // ... }
Defina o tipo de
return
pararegisterRequest()
e todas as funções filhas comoJSONObject
.suspend fun registerRequest(sessionId: String): ApiResult<JSONObject> { val call = client.newCall( Request.Builder() .url("$BASE_URL/<your api url>") .addHeader("Cookie", formatCookie(sessionId)) .method("POST", jsonRequestBody { name("attestation").value("none") name("authenticatorSelection").objectValue { name("authenticatorAttachment").value("platform") name("userVerification").value("required") name("residentKey").value("required") } }).build() ) val response = call.await() return response.result("Error calling the api") { parsePublicKeyCredentialCreationOptions( body ?: throw ApiException("Empty response from the api call") ) } }
Remova com segurança da sua visualização todos os métodos que processam chamadas de tela de início de intents e de resultados de atividades.
Como
registerRequest()
agora retorna umJSONObject
, não é necessário criar umaPendingIntent
. Substitua a intent retornada por umJSONObject
. Atualize as chamadas da tela de início da intent para chamarcreateCredential()
na API Credential Manager. Chame o método de APIcreateCredential()
.suspend fun createPasskey( activity: Activity, requestResult: JSONObject ): CreatePublicKeyCredentialResponse? { val request = CreatePublicKeyCredentialRequest(requestResult.toString()) var response: CreatePublicKeyCredentialResponse? = null try { response = credMan.createCredential( request as CreateCredentialRequest, activity ) as CreatePublicKeyCredentialResponse } catch (e: CreateCredentialException) { showErrorAlert(activity, e) return null } return response }
Quando a chamada for concluída, envie a resposta de volta ao servidor. A solicitação e a resposta dessa chamada são semelhantes à implementação do FIDO2. Portanto, nenhuma mudança é necessária.
Autenticar com chaves de acesso
Depois de configurar a criação da chave de acesso, você pode configurar seu app para permitir que os usuários façam login e se autentiquem usando as chaves de acesso. Para fazer isso, atualize o código de autenticação para processar os resultados do Gerenciador de credenciais e implemente uma função para autenticar usando chaves de acesso.
- A chamada de solicitação de login ao servidor para receber as informações necessárias a serem
enviadas à solicitação
getCredential()
é a mesma que a implementação do FIDO2. Nenhuma mudança é necessária. Semelhante à chamada de solicitação de registro, a resposta retornada está no formato JSONObject.
/** * @param sessionId The session ID to be used for the sign-in. * @param credentialId The credential ID of this device. * @return a JSON object. */ suspend fun signinRequest(): ApiResult<JSONObject> { val call = client.newCall(Builder().url(buildString { append("$BASE_URL/signinRequest") }).method("POST", jsonRequestBody {}) .build() ) val response = call.await() return response.result("Error calling /signinRequest") { parsePublicKeyCredentialRequestOptions( body ?: throw ApiException("Empty response from /signinRequest") ) } } /** * @param sessionId The session ID to be used for the sign-in. * @param response The JSONObject for signInResponse. * @param credentialId id/rawId. * @return A list of all the credentials registered on the server, * including the newly-registered one. */ suspend fun signinResponse( sessionId: String, response: JSONObject, credentialId: String ): ApiResult<Unit> { val call = client.newCall( Builder().url("$BASE_URL/signinResponse") .addHeader("Cookie",formatCookie(sessionId)) .method("POST", jsonRequestBody { name("id").value(credentialId) name("type").value(PUBLIC_KEY.toString()) name("rawId").value(credentialId) name("response").objectValue { name("clientDataJSON").value( response.getString("clientDataJSON") ) name("authenticatorData").value( response.getString("authenticatorData") ) name("signature").value( response.getString("signature") ) name("userHandle").value( response.getString("userHandle") ) } }).build() ) val apiResponse = call.await() return apiResponse.result("Error calling /signingResponse") { } }
Remova com segurança qualquer método que processe a tela de início de intents e chamadas de resultado de atividades da sua visualização.
Como
signInRequest()
agora retorna umJSONObject
, não é necessário criar umaPendingIntent
. Substitua a intent retornada por umJSONObject
e chamegetCredential()
nos métodos da API.suspend fun getPasskey( activity: Activity, creationResult: JSONObject ): GetCredentialResponse? { Toast.makeText( activity, "Fetching previously stored credentials", Toast.LENGTH_SHORT) .show() var result: GetCredentialResponse? = null try { val request= GetCredentialRequest( listOf( GetPublicKeyCredentialOption( creationResult.toString(), null ), GetPasswordOption() ) ) result = credMan.getCredential(activity, request) if (result.credential is PublicKeyCredential) { val publicKeycredential = result.credential as PublicKeyCredential Log.i("TAG", "Passkey ${publicKeycredential.authenticationResponseJson}") return result } } catch (e: Exception) { showErrorAlert(activity, e) } return result }
Depois que a chamada for bem-sucedida, envie a resposta de volta ao servidor para validar e autenticar o usuário. Os parâmetros de solicitação e resposta para essa chamada de API são semelhantes à implementação do FIDO2. Portanto, nenhuma mudança é necessária.
Outros recursos
- Referência de exemplo do Gerenciador de credenciais
- Codelab do Gerenciador de credenciais
- Como trazer a autenticação integrada para seus apps com chaves de acesso usando a API Credential Manager
- Codelab do FIDO2