Introdução
A verificação em dois fatores é amplamente utilizada, visto que garante maior segurança tanto para a empresa quanto para o usuário, pois confirma que o recurso está sendo acessado pelo titular da conta. Com isso, foi desenvolvida uma API que permite a leitura de SMS recebidos em tempo real. Neste artigo, abordaremos essa forma de verificação e como ela é suportada usando conceitos de broadcast receiver.
Prós e contras da API
Como todo recurso de software, existe prós e contras e deve ser resguardado para se utilizar em uma ocasião que realmente traz mais benefício do que malefício.
Os pontos fortes da API SMS Retriever são:
- Sem permissões no manifest
- API mantida pela criadora do android, a Google;
- Fácil implementação.
Os pontos contras:
- Permissão de consentimento único. Enquanto o aplicativo estiver em execução, só consegue ler o SMS única vez;
- Documentação da API desatualizada com alguns métodos;
- É necessário fazer manipulações para pegar o SMS e implementar um regex para pegar o código de verificação.
O que de fato é essa API e por que deve usar ela?
A API SMS Retriever, é o principal e único recurso de aplicativos que não tem como objetivo principal o tratamento de mensagens e ligações. Com isso, em um aplicativo que se adequam a esses requisitos, como Whatsapp, Telegram e entre outros conseguem fazer a verificação por SMS através de apenas um broadcast receiver e permissões, seja em tempo de execução (programáticamente) quanto no manifest. Antes, era bem comum os aplicativos pegarem o SMS do usuário com essas permissões, mas em 2018, a Google tirou essa flexibilização devido o uso indevido do recurso.
Implementação
- Dependências: Adicione no build.gradle do app essas dependências:
implementation 'com.google.android.gms:play-services-auth:17.0.0'
implementation 'com.google.android.gms:play-services-auth-api-phone:17.4.0'
- Declarando as váriaveis
private lateinit var smsClient: SmsRetrieverClient
private val broadcastReceiver = SmsBroadcastReceiver()
private lateinit var resultLauncher: ActivityResultLauncher<Intent>
- Instanciando e registrando o SmsClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
resultLauncher()
smsClient = SmsRetriever.getClient(this@MainActivity)
val actionId = IntentFilter()
val intentFilter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
registerReceiver(broadcastReceiver, intentFilter)
configureFieldsWithSMS()
initSmsClient()
}
- Criando listeners de sucesso e erro para o usuário:
interface States {
fun onSuccess(consentIntent: Intent)
fun error(typeError: TypeError)
companion object {
var statesResult: States? = null
fun instanceStates(states: States) {
statesResult = states
}
}
}
- Colocando para detectar o primeiro SMS a ser recebido
Caso dê erro, entra no onFailure e se der sucesso para poder detectar os SMS a ser recebido, entra no onSuccess. Caso não receba um SMS a 5 minutos, ele entra em Timeout.
fun initSmsClient() {
smsClient.startSmsUserConsent(null).addOnSuccessListener {
Toast.makeText(this, "Esperando pelo SMS", Toast.LENGTH_LONG).show()
}.addOnFailureListener {
Toast.makeText(this, it.message.toString(), Toast.LENGTH_LONG).show()
it.printStackTrace()
}
}
- Configurando o broadcast receiver
Para processar as transmissões e eventos, é necessário utilizar um broadcast receiver, registrar ele na activity e passar um IntentFilter (assim como está no onCreate)
class SmsBroadcastReceiver() : BroadcastReceiver() {
private fun <T>configureParcelable(parcelable: String, intent: Intent, classParcelable: Class<T>) : T? {
return if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.extras?.getParcelable(parcelable, classParcelable)
}else{
intent.extras?.getParcelable(parcelable)
}
}
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == SmsRetriever.SMS_RETRIEVED_ACTION) {
try {
val smsRetrieverStatus = configureParcelable(SmsRetriever.EXTRA_STATUS, intent, Status::class.java)
when (smsRetrieverStatus?.statusCode) {
CommonStatusCodes.SUCCESS -> {
val consentIntent = configureParcelable(SmsRetriever.EXTRA_CONSENT_INTENT, intent, Intent::class.java)
consentIntent?.let { States.statesResult?.onSuccess(it)}
}
CommonStatusCodes.ERROR -> {
States.statesResult?.error(TypeError.ERROR)
}
CommonStatusCodes.TIMEOUT -> {
States.statesResult?.error(TypeError.TIMEOUT)
}
}
} catch (exception: java.lang.Exception) {
States.statesResult?.error(TypeError.EXCEPTION)
exception.printStackTrace()
}
}
}
}
- Apresentando o SMS para o usuário
private fun configureFieldsWithSMS() {
States.instanceStates(object : States {
override fun onSuccess(consentIntent: Intent) {
resultLauncher.launch(consentIntent)
}
override fun error(typeError: TypeError) {
Log.d("ERROR", typeError.name)
}
})
}
- Processando resposta do usuário: Caso o usuário deu OK ao consentimento de ler SMS, conseguiremos ler a mensagem. Com isso, é necessário resgatar esse código no activityResult
private fun resultLauncher() {
resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result->
if(result.resultCode == Activity.RESULT_OK) {
val sms = result.data?.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE).toString()
Toast.makeText(this@MainActivity, sms, Toast.LENGTH_LONG).show()
}
}
}
- Criando a mensagem no emulador: Para simular, é necessário seguir um padrão de mensagem especificada pela Google.
Com isso, já que não temos um server side para mandar o SMS com códigos, iremos configurar manualmente no emulador e mandar por lá mesmo.
Clique nas configurações do emulador (3 pontos):
Vá na opção Phone do lado esquerdo e depois insira um texto com os padrões especificados acima. Exemplo: teste 1234.
Com isso, será apresentando o bottomSheet pedindo o consentimento:
E caso o usuário dê o aceite, o SMS será apresentando na tela em forma de Toast:
Caso queira ver o código todo, ele se encontra neste repositório do github.
Contate-me
Linkedin
Top comments (0)