Autenticação por SMS sem senha usando Xamarin

April 21, 2015
Escrito por

Autenticação por SMS sem senha usando Xamarin

Temos que admitir: as senhas são um fardo. Se uma senha é fácil o suficiente para ser lembrada, provavelmente não é segura. Isso nos leva a usar aplicativos como o 1Password para gerenciar nossas senhas mais seguras. Não é o ideal, mas funciona. Funciona bem, até os grandes sites terem seus bancos de dados invadidos. É provável que alguém já tenha visto uma de suas senhas preferidas. Se você não acredita em mim, dê só uma olhada em haveibeenpwned.com. Se você está fora de perigo, tem muita sorte. Como solução de segurança, as senhas por si só são falhas. Precisamos mesmo delas?

Recentemente, Ricky Robinett escreveu um post no blog detalhando como criar uma solução de autenticação sem senha usando PHP com Laravel. Ele se inspirou na implementação do app Cover para essa ideia e se ver livre das senhas. Neste post, vamos criar um front-end móvel compatível com várias plataformas no Xamarin que usa o método de autenticação descrito no post do Ricky. O Xamarin é uma plataforma para o desenvolvimento de aplicativos móveis na linguagem C#. Vamos usar um recurso da plataforma chamado Xamarin.Forms, que vai nos permitir escrever a interface do usuário em um projeto compartilhado. Esse projeto compartilhado vai gerar a UI para iOS, Android e Windows Phone com o mesmo código-fonte. Quando terminarmos, seus aplicativos para iOS, Android e Windows Phone poderão usar essa técnica para remover senhas do fluxo de trabalho de login.

Ferramentas

Para você acompanhar o código completo, preparei este repositório do Github.

Como o back-end funciona?

Este post se concentrará em criar um front-end compatível com várias plataformas que faz logins em um back-end muito simples, conforme descrito neste tutorial. O PHP não é uma primeira escolha óbvia para um desenvolvedor de linguagem de programação C#, por isso transferi esse back-end para trabalhar no ASP.NET MVC. O back-end tem os seguintes endpoints:

  • /user/validate/ – As solicitações POST para esse endpoint com um número de telefone confirmam que uma conta de usuário está associada a esse número e enviam um token de verificação.
  • /user/auth/ – As solicitações POST para esse endpoint com um token validam que o token está correto para a sessão atual.
  • /profile – As solicitações GET para esse endpoint retornarão alguns dados supersecretos. Esse endpoint só pode ser acessado depois que um usuário for autenticado.

Como estamos focados no front-end e Ricky fez um excelente trabalho detalhando o funcionamento do processo, recomendo enfaticamente que você leia o tutorial dele. A versão do .NET especificada acima é rica em comentários e coincide com os endpoints do post.

Configurar o aplicativo Xamarin.Forms

Vamos começar criando um aplicativo Xamarin.Forms. Há duas opções para criar um aplicativo Xamarin.Forms; a diferença está na forma de compartilhar o código. Se você ficou curioso sobre as diferenças entre as duas opções, a documentação do Xamarin tem uma ótima explicação. Se você quiser ter compatibilidade com Windows Phone, precisará usar o Visual Studio para criar o aplicativo. Vamos usar a opção "Blank App (Xamarin.Forms Portable)" (App em branco [Xamarin.Forms Portable]) para criar nosso aplicativo e nomeá-lo como PasswordlessLogin:

Tela de novo projeto

Uma solução Xamarin.Forms consiste em uma biblioteca de classe portátil (PCL) que vai armazenar o código de UI compartilhado do aplicativo e um projeto específico de plataforma para iOS, Android e Windows Phone. Observação: para compatibilidade com Windows Phone, o projeto deve ser criado e compilado no Visual Studio. Vamos trabalhar exclusivamente no projeto da PCL, mostrado na seguinte captura de tela:

Estrutura do Projeto

Se o node Packages (Pacotes) do seu projeto disser que tem atualizações como na captura de tela acima, significa que há uma atualização disponível para Xamarin.Forms. Clique com o botão direito do mouse na solução e clique em Update NuGet Packages (Atualizar pacotes NuGet). Se você estiver usando o Visual Studio, clique com o botão direito do mouse na solução e clique em Manage NuGet Packages… (Gerenciar pacotes NuGet…). Clique no node Update (Atualizar) na lista e em Update All (Atualizar todos):

Aba Updates

Agora, vamos adicionar alguns pacotes NuGet ao projeto. Precisamos dos seguintes pacotes para nosso aplicativo:

  • Microsoft HTTP Client Libraries: adiciona suporte para o objeto HttpClient que vamos usar para fazer solicitações ao back-end pelo app
  • Json.NET: para analisar os resultados de JSON retornados pelo back-end no processo de autenticação

Para instalar um pacote, clique com o botão direito do mouse no node Packages (Pacotes) do projeto PasswordlessLogin e selecione Add Packages… (Adicionar pacotes...). Procure o nome do pacote na caixa de pesquisa e adicione-o clicando no botão Add Package (Adicionar pacote) mostrado abaixo:

Xamarin Studio:

Adicionando pacote

No Visual Studio, clique com o botão direito do mouse na solução, clique em Manage NuGet Packages for Solution… (Gerenciar pacotes NuGet da solução…) e adicione os pacotes mostrados abaixo:

Adicionando pacote

Com esses pacotes, temos tudo o que precisamos para começar a criar o aplicativo.

Qual é o seu número?

O processo de autenticação sem senha começa com o envio de uma solicitação POST com o número de telefone do usuário para /user/validate. A primeira coisa que precisamos implementar é uma tela para o usuário digitar esse número de telefone. Uma tela é chamada de Page (Página) no Xamarin.Forms; há duas maneiras de criá-la. Uma delas é usando código em C#. Abra o arquivo PasswordlessLogin.cs no projeto principal para ver o seguinte código no construtor de classe App (se você estiver usando o Visual Studio, o nome desse arquivo poderá ser App.cs):

// The root page of your application
MainPage = new ContentPage {
    Content = new StackLayout {
        VerticalOptions = LayoutOptions.Center,
        Children = {
            new Label {
                XAlign = TextAlignment.Center,
                Text = "Welcome to Xamarin Forms!"
            }
        }
    }
};

Essa é a aparência da API baseada em código para a criação da UI. Em nosso aplicativo, vamos usar o segundo método para criar a UI: XAML. XAML (eXtensible Application Markup Language, linguagem de marcação de aplicativo extensível) é uma linguagem de marcação que permite criar a UI usando uma sintaxe declarativa. Embora o suporte a ferramentas seja mínimo neste momento, no futuro, a XAML permitirá que Xamarin.Forms tenha ótimas ferramentas para a criação da UI, por isso é recomendável investir tempo para aprender sobre ela.

Clique com o botão direito do mouse no projeto PasswordlessLogin, selecione Add->New File… (Adicionar->Novo arquivo...) (Add-> New Item… [Adicionar->Novo item...] no VS) e adicione um Forms ContentPage Xaml (ou Forms Xaml Page no VS) chamado PhoneNumberPage:

Adicionando página xaml

Isso criará dois arquivos: PhoneNumberPage.xaml e PhoneNumberPage.xaml.cs. O primeiro conterá nossa marcação de UI e o último o código que funciona com os objetos da UI. Abra o arquivo PhoneNumberPage.xaml e substitua o conteúdo pela seguinte marcação:

hl_lines="7  8  9  10  11"
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="PasswordlessLogin.PhoneNumberPage"
             Title="Enter Your Phone Number"
             Padding="10,0,10,0">
  <StackLayout VerticalOptions="Center" HorizontalOptions="Fill">
    <Label Text="Phone number" HorizontalOptions="Start"/>
    <Entry Placeholder="+15555551234" HorizontalOptions="Fill" x:Name="PhoneNumberEntry"/>
    <Button Text="NEXT" HorizontalOptions="End" x:Name="NextButton"/>
  </StackLayout>
</ContentPage>

Essa marcação especifica que o conteúdo da página será um StackLayout que preenche a tela e centraliza o conteúdo na vertical. Tudo o que for adicionado a esse layout será empilhado na vertical. Dentro desse StackLayout, adicionamos um rótulo, uma entrada e um botão. Fornecemos um x:Name para a entrada e o botão, para podermos referenciá-los no arquivo codebehind (PhoneNumberPage.xaml.cs). Isso é tudo o que precisamos fazer para criar o layout da UI para PhoneNumberPage. No entanto, se iniciarmos o aplicativo agora, não veremos essa página porque não a definimos como página principal do app Xamarin.Forms. Para fazer isso, abra o arquivo PasswordlessLogin.cs (ou App.cs no template (modelo) do Visual Studio) e substitua o construtor de classe App pelo seguinte código:

public App()
{
    // The root page of your application
    MainPage = new NavigationPage(new PhoneNumberPage());
}

Esse código cria uma NavigationPage, o que nos permitirá avançar e voltar nas páginas do app e define a página raiz para que a navegação seja uma instância de PhoneNumberPage. Agora, é uma boa ideia fazer um teste executando o aplicativo para garantir que a UI esteja configurada corretamente. Escolha um projeto específico da plataforma da sua preferência (PasswordlessLogin.iOS, PasswordlessLogin.Droid ou PasswordlessLogin.WinPhone) e defina-o como projeto de inicialização clicando com o botão direito do mouse no node do projeto e selecionando Set As Startup Project (Definir como projeto de inicialização). Ao executar o aplicativo (no dispositivo ou em um simulador), você verá a seguinte tela:

Primeira tela do app

Você pode inserir um número de telefone no campo de entrada, mas não acontecerá nada se tocar em NEXT (AVANÇAR), porque ainda não escrevemos o código. Vamos fazer isso agora. Abra o arquivo PhoneNumberPage.xaml.cs e adicione as seguintes instruções using:

using System.Net.Http;
using Newtonsoft.Json;

Em seguida, vamos adicionar um campo para um objeto HttpClient:

HttpClient client;

Adicione uma linha ao construtor para conectar o evento Clicked ao NextButton:

NextButton.Clicked += NextButton_Clicked;

Por fim, adicione o manipulador de eventos do botão:

private async void NextButton_Clicked(object sender, EventArgs e)
{
    client = new HttpClient ();
    var content = new FormUrlEncodedContent(new[] 
        {
            new KeyValuePair<string, string>("phone_num", PhoneNumberEntry.Text)
        });
    var result = await client.PostAsync ("https://*** Your backend URL***/user/validate", content);

    if (result.StatusCode == System.Net.HttpStatusCode.OK) {
        var definition = new { Success = true };
        var response = JsonConvert.DeserializeAnonymousType (result.Content.ReadAsStringAsync().Result, definition);

        if (response.Success) {
            DisplayAlert ("Phone Number Valid", "You entered a valid phone number!", "OK");
        }
        else {
            DisplayAlert ("Phone Number Invalid", "You entered an invalid phone number, try again.", "OK");
        }
    } else {
        DisplayAlert ("Backend Problem", "Did not receive successful response from backend.", "OK");
    }
}

O manipulador de eventos cria uma instância de HttpClient. Em seguida, ele cria um KeyValuePair contendo o número de telefone inserido no campo PhoneNumberEntry a ser passado como conteúdo codificado em formulário para o back-end. Depois, ele faz uma solicitação POST para o endpoint /user/validate e verifica se o resultado é um valor Success de true. Atualize o URL na chamada PostAsync para o URL do seu back-end. Até agora, exibimos um pop-up de sucesso ou falha porque ainda não criamos a próxima página. Execute o aplicativo e teste com seu número de telefone (use o formato "+15555551234"). Se você receber o alerta de número válido e uma mensagem de texto com um token for enviada para seu telefone, você estará pronto para a próxima etapa.

Este jogo exige tokens

Na etapa anterior, recebemos um token via SMS. Para concluir o processo de autenticação, precisamos permitir que o usuário insira esse token na próxima página do aplicativo. Agora, vamos criar a página de entrada de token adicionando ao aplicativo um novo arquivo Forms ContentPage Xaml chamado TokenPage. Abra o arquivo TokenPage.xaml e substitua o conteúdo pela seguinte marcação:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="PasswordlessLogin.TokenPage"
             Title="Verify Token">
  <StackLayout VerticalOptions="Center" HorizontalOptions="Fill" Padding="10,0,10,0">
    <Label Text="Token" HorizontalOptions="Start"/>
    <Entry HorizontalOptions="Fill" x:Name="TokenEntry"/>
    <Button Text="NEXT" HorizontalOptions="End" x:Name="NextButton"/>
  </StackLayout>
</ContentPage>

Essa marcação deve parecer muito familiar porque é quase idêntica à tela anterior. A única diferença é que o campo Entry aceita um token em vez de um número de telefone nessa página. Vamos trabalhar no envio desse token ao back-end para fins de verificação. Abra o arquivo TokenPage.xaml.cs e adicione as seguintes instruções using:

using System.Net.Http;
using Newtonsoft.Json;

Em seguida, adicione um campo para um objeto HttpClient à classe TokenPage:

HttpClient client;

Substitua o constructor (construtor) pelo seguinte código:

public TokenPage(HttpClient myclient)
{
    client = myclient;
    InitializeComponent();
    NextButton.Clicked += NextButton_Clicked;
}

Desta vez, estamos passando uma instância de HttpClient para a página em vez de criar uma. Isso é porque o back-end usa cookies e queremos usar a mesma sessão ao fazer a solicitação de token. Agora, adicione o manipulador de eventos NextButton_Clicked:

private async void NextButton_Clicked(object sender, EventArgs e)
{
    var content = new FormUrlEncodedContent(new[] 
        {
            new KeyValuePair<string, string>("token", TokenEntry.Text)
        });

    var result = await client.PostAsync ("https://*** Your backend URL ***/user/auth", content);

    if (result.StatusCode == System.Net.HttpStatusCode.OK) {
        var definition = new { Success = true };
        var response = JsonConvert.DeserializeAnonymousType (result.Content.ReadAsStringAsync ().Result, definition);

        if (response.Success) {
            DisplayAlert ("Successful Auth", "You have authenticated successfully!", "OK");
        }
    } else {
        DisplayAlert ("Backend Problem", "Did not receive successful response from backend.", "OK");
    }
}

Esse código também é quase idêntico à página anterior; a diferença é que agora estamos passando um valor de token para verificação do endpoint /user/auth. Atualize o URL na chamada PostAsync para o URL do back-end. Se Success for true, vamos exibir um alerta indicando sucesso. Em um aplicativo real, provavelmente o usuário acessaria a parte protegida do aplicativo. Uma última coisa que precisamos fazer é substituir Alert em PhoneNumberPage por uma navegação até TokenPage em caso de uma entrada de número de telefone bem-sucedida. Edite a verificação de resposta no manipulador NextButton_Clicked de PhoneNumberPage.xaml.cs para conter o seguinte:

if (response.Success) {
    await Navigation.PushAsync (new TokenPage (client));
}
else {
    DisplayAlert ("Phone Number Invalid", "You entered an invalid phone number, try again.", "OK");
}

A linha realçada cria um novo TokenPage passando o objeto HttpClient e segue na pilha de navegação do app.

Agora, todo o aplicativo está pronto para os testes. Execute-o na plataforma de sua preferência para testá-lo. Digite seu número de telefone e clique em NEXT (AVANÇAR). Na próxima página, digite o token recebido e clique em NEXT (AVANÇAR). Se tudo funcionar bem, você verá uma mensagem de alerta indicando sucesso:

Tela final com autenticação em funcionamento

Próximas etapas

Grite aos quatro cantos do mundo que é o fim das senhas! Ou, pelo menos, pode ser que agora criamos uma solução de autenticação sem senha que funciona nas três principais plataformas móveis. O que poderíamos fazer para aprimorar a solução criada neste post:

  • Criar uma página de registro para novos usuários
  • Fornecer um método de autenticação de backup caso o usuário não tenha acesso a SMS
  • Implementar uma solução de voz para fornecer o token, além do SMS
  • Criar uma página de perfil para o usuário que ele só poderá acessar depois que for devidamente autenticado

Estou muito empolgado para saber o que você está criando com a plataforma Twilio. Fique à vontade para entrar em contato comigo pelo e-mail lleao@twilio.com ou pelo Twitter @luisleao.

Este artigo foi traduzido do original "Passwordless SMS Authentication Using Xamarin". Enquanto melhoramos nossos processos de tradução, adoraríamos receber seus comentários em help@twilio.com - contribuições valiosas podem render brindes da Twilio.