09 outubro 2008

Importando sua lista de contatos de email para o Pagestacker sem deixar o Pagestacker saber a sua senha de email

Como eu já mencionei antes aqui, eu sou um dos criadores do Pagestacker, um aplicativo web onde os usuários podem guardar suas páginas favoritas da web para referência futura e também para pesquisar o conjunto de todas as páginas guardadas pelos demais usuários (mas de forma completamente anônima, isto é, sem identificar quem guardou cada página).

Uma funcionalidade freqüentemente solicitada pelos nossos usuários, e que desenvolvemos recentemente, era a possibilidade de se enviar por email páginas guardadas na própria conta, diretamente de dentro do Pagestacker.

Para que essa funcionalidade ficasse realmente útil, era necessário que ela viesse acompanhada da funcionalidade de auto-completar, que oferece nomes e endereços de email de destinatários para o usuário enquanto ele preenche o campo correspondente a essa informação no email, mostrando possíveis complementos para o que ele vai digitando. Se uma das sugestões for a que ele queria usar, basta clicar nela para terminar de preencher o campo.

Parece pouco, mas é algo que traz uma enorme economia de tempo, incentiva a recorrência do envio de páginas via email e que faz muita falta depois que você a experimenta.

De fato, trata-se de uma funcionalidade presente nos principais serviços gratuitos de email na web hoje em dia. Não fazia sentido, portanto, oferecer uma experiência de envio de conteúdo via email sem ela, ou seja, uma experiência de uso inferior àquela a que muitos dos nossos usuários já estão acostumados.

Bom, para que o auto-completar funcione, é preciso que o usuário primeiro crie uma lista com os nomes e os endereços de emails dos seus destinatários. Pedir ao usuário que preencha essa lista, nome por nome, também não faz sentido.

Por outro lado, é provável que o usuário já tenha uma lista dessas em sua conta de email. "Legal!", pensamos, "Vamos dar um jeito de o usuário poder importar essa lista para o Pagestacker!".

O Problema

Uma forma cada vez mais comum de se fazer isso é pedindo ao usuário que forneça seu endereço de email e a senha dessa conta de email. Daí, o serviço em que o usuário forneceu esses dados pode enviar um programa até a conta de email dele e copiar essa lista de contatos.

Redes sociais como o MySpace, por exemplo, fazem isso o tempo todo a fim de "ajudar" você a trazer seus amigos para o serviço deles.

Nesses casos, o serviço em questão sempre diz que o usuário pode confiar porque a senha não será armazenada. Sua senha é apenas "emprestada" por uns instantes enquanto o processo de importação é realizado.

Alguém acha que há um problema sério com essa abordagem? =)

O que você pensaria de alguém que pedisse sua conta no banco ou as chaves da sua casa para pegar alguma coisa lá pra você, ainda que com a garantia de que essa senha não seria armazenada ou que não seriam feitas cópias das suas chaves?

Bom, por mais honesto que um serviço como o MySpace possa ser (e eu não tenho razões para duvidar de que seja mesmo), ninguém deveria ensinar para os usuários que é ok "emprestar" sua senha, de qualquer serviço que seja, para sites de terceiros por aí, mesmo que seja para realizar uma tarefa útil para eles. Não é possível garantir que todos os sites em que o seu usuário possa vir a ter essa informação solicitada no futuro serão idôneos.

Quando o usuário entrega essa informação, ele fica impotente. Ele não tem como verificar com absoluta certeza que a sua senha não foi usada para ações não autorizadas ou que ela não foi armazenada.

Se o serviço que pede ao usuário a senha da conta de email dele afirma sinceramente que não usará essa senha para nada que o usuário não autorize, o correto seria fazer isso de um jeito em que, mesmo que o serviço tentasse violar essa confiança, não conseguisse fazer isso.

Deve haver uma forma de fazer isso e colocar o usuário no controle da situação, respeitando sua privacidade.

Solução: Autenticação Delegada

Ao usar a função de importar sua lista de contatos da sua conta de email para o Pagestacker, o usuário é direcionado para o próprio serviço de email em que ele mantém sua conta. Ao chegar lá, o serviço avisa ao usuário que o Pagestacker pediu para importar a lista de contatos dele e pergunta se ele autoriza a ação.

Se o usuário decidir autorizar, é o próprio serviço de email quem pede a senha da conta de email do usuário. Repetindo: o usuário só precisa informar a senha da conta de email dele para o seu próprio serviço de email.

O Pagestacker nunca fica sabendo qual é a senha do usuário.

Daí, o serviço de email envia a lista de contatos para a conta do usuário no Pagestacker e encerra essa conexão com o Pagestacker. Nenhuma outra informação pode ser trocada entre o serviço de email e o Pagestacker sem uma nova autorização do usuário.

Esse processo é chamado de autenticação delegada. O usuário delega ao seu serviço de email a tarefa de verificar que a solicitação do Pagestacker é autêntica, isto é, feita a pedido do próprio usuário.

E como o usuário pode confiar que é exatamente isso que está acontecendo?

Bom, todo o processo é na verdade uma funcionalidade oferecida pelo serviço de email do usuário. O Pagestacker apenas segue as regras que o seviço de email tiver estabelecido para realizar essa ação. Se o usuário já confia no serviço de email para mediar e armazenar suas comunicações particulares, não há razão para não confiar nele para mais essa funcionalidade.

Por essa razão, o Pagestacker só pode oferecer essa funcionalidade para serviços de email que também já a ofereçam. No momento, são apenas 3: Gmail, Yahoo Mail e Hotmail (MSN e Windows LIve também).

E como confiar que a tela em que o usuário informa a senha é mesmo uma tela do seu serviço de email?

O usuário deve sempre verificar o endereço da página que o seu navegador mostra para ter certeza de que está mesmo no lugar certo e não em um lugar "disfarçado" como uma tela de seu serviço de email a fim de maliciosamente roubar sua senha.

Para programadores: como fizemos

Nesse trecho final deste artigo, eu descrevo como desenvolvemos o software que realiza o que eu descrevi acima. Se você não é programador, dificilmente achará interessante. Basta lembrar que você pode importar sua lista de contatos para o Pagesatcker sem deixar o Pagestacker saber sua senha de email. =)

Bom, a melhor forma de se implementar a autenticação delegada é através do protocolo OAuth. Trata-se de um protocolo aberto, isto é, que todos podem usar gratuitamente e cujo funcionamento exato é de conhecimento público (o que não significa que ele possa ser violado). O objetivo de seus criadores é fazer dele um padrão, usado por todos os desenvolvedores. Quanto mais adeptos, mais simples fica para desenvolver aplicativos que possam estabelecer autenticação delegada entre si, uma vez que basta implementar sua especificação a fim de se alcançar compatibilidade praticamente universal.

Ótimo, não é? Sim, mas infelizmente nenhum dos 3 serviços de email com quem o Pagestacker estabelece autenticação delegada "conversa" através do OAuth. =)

Cada um deles implementou sua própria forma de autenticação delegada, obrigando-nos a ter 3 vezes o trabalho que teria sido necessário caso todos eles seguissem um mesmo protocolo (de preferência, um que já fosse usado por tantos outros desenvolvedores quanto possível, como o OAuth).

Pra começar, procurei saber se já havia alguma implementação disponível na web de clientes que consumissem essas diferentes API's (reuso de código é uma virtude, meu amigos =D).

Depois de uma boa pesquisa, achei uma biblioteca chamada Contacts, dentro do Github. O problema é que ela implementava cliente apenas para consumir a API do Gmail. Logo, tive que construir os nossos clientes para Yahoo Mail e Hotmail (e MSN e Windows Live)!

Como resultado disso, fiz um fork do projeto Contacts do Mislav. Dentro do meu fork, eu implementei clientes para a API do Hotmail (que também servem para MSN e Windows Live) e para a API do Yahoo. Além disso, adaptei um pouco melhor o cliente da API do Gmail para os nossos requisitos. Fiz o projeto inteiro em Ruby e é totalmente open source.

Programando

Agora vou mostrar um exemplo de como é fácil usar o projeto que desenvolvi para importar contatos do Yahoo. Antes de tudo, faça download ou git-clone do projeto que está aqui.

(os códigos publicados abaixo só são visualizados no próprio blog, não é possível visualizá-los no seu leitor de feeds)

1. Primeiro, registre sua aplicação aqui. Basta seguir os passos e anotar uns dados de que vamos precisar depois: o seu appid e seu secret.

2. Depois, basta configurar o arquivo config.yml que está dentro do projeto Contacts com os dados do cadastro de sua aplicação dentro do Yahoo.



3. Agora você deve usar o código abaixo para gerar a Authentication URL. Essa é a URL que você deve mostrar para seu usuário. Ao clicar nela, o usuário será redirecionada para o Yahoo, onde ele irá precisar logar.



4. Após logar no Yahoo e dar permissão para sua aplicação acessar os contatos do Yahoo, o Yahoo irá redirecionar o usuário de volta para sua aplicação. Ele fará isso via um HTTP Get. No path dessa requisição está a token necessária para importar os contatos de e-mail do seu usuário.




Pronto! Isso é tudo que você precisa para poder importar os contatos do seu usuário. Assim você evita fazer screen scraping, o que evita também a necessidade de refazer seu trabalho sempre que algum dos serviços do qual você importa dados faz qualquer alteração em seu layout de tela e, principalmente, você respeita a privacidade dos dados do seu usuário.

Como eu disse, todos os códigos acima estão disponíveis como software open source no Github.

É assim que o Pagestacker faz! Nós vamos a fundo a fim de oferecer a melhor experiência de uso para os nossos usuários, sem comprometer a sua privacidade.

Graças aos conceitos de OAuth, autenticação delegada e uma boa engenharia por trás do código do Pagestacker, você já pode compartilhar suas páginas salvas de um modo fácil e prático. =)


Últimas observações

O Pagestacker oferece também a funcionalidade de importação de páginas favoritas previamente armazenadas no navegador do usuário além de importação de um outro aplicativo web chamado Delicious. Nesse segundo caso, é necessário o fornecimento da senha da conta do usuário no Delicious.

O fornecimento dessa senha é realizado dentro do Pagestacker.

O que é isso? Uma incoerência?

Na verdade, não.

O Pagestacker não faz screen scraping da conta do usuário no Delicious. O Pagestacker usa a API que o próprio Delicious oferece para essa finalidade. É tudo feito de acordo com as regras do Delicious.

É o Delicious que não oferece suporte a autenticação delegada, seja através do protocolo OAuth ou de um macanismo próprio. Como afirmei acima, o Pagestacker só pode estabelecer autenticação delegada com sites que também oferecem esse recurso.

Nós adoraríamos que a API do Delicious funcionasse através de autenticação delegada. =)

No entanto, entre não oferecer a funcionalidade de forma nenhuma ou seguir as regras da API do próprio Delicious, acreditamos que é do melhor interesse dos nossos usuários ficar com a segunda opção (e é claro, os usuários podem confiar que o Pagestacker não armazenará sua senha do Delicious nem a usará para nenhum outro fim ;-) ).


Atualização: Atualmente o Gmail já disponibiliza uma API usando OAuth.



 
Fork me on GitHub