APIs e Microsserviços

14 mai, 2014

Implementação rápida de login social com Twitter

Publicidade

Implementar login social com Twitter é uma ótima maneira de integrar a experiência do usuário da sua aplicação, ou produto, com a plataforma do Twitter. Além disso, você possibilita que os usuários compartilhem conteúdo produzido pela sua aplicação.

Uma característica importante ao implementar o login social com o Twitter é o uso de OAuth, um padrão largamente adotado e que permite o acesso à recursos do usuário sem a necessidade de compartilhar as suas senhas. Porém, mesmo quando as especificações de como implementar OAuth são super detalhadas e claras, essa tarefa pode se transformar em algo complexo e frustrante.

Por isso resolvemos construir um código em Ruby e Python que exemplifica bem como funciona esse fluxo. Dessa forma, você pode começar a sua implementação de forma bem mais rápida e confiante. Os exemplos são bem fáceis de instalar e testar.

Nós vamos fazer um tour pela implementação em Ruby, que é uma aplicação feita usando o framework Sinatra. Perceba que esse código acompanha o procedimento documentado no site de desenvolvedores do Twitter.

Passo 1: Obtendo o request token

Quando o usuário clica em “Login” na sua aplicação, o primeiro passo é obter um request token do Twitter:

Listagem 1:

https://github.com/lfcipriani/sign_in_with_twitter_sample/blob/master/app.rb#L48

get '/signin' do
 # After hitting Sign in link, first thing your app must do
 # is to get a request token.
 # See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 1)
 token = TwitterSignIn.request_token
# With request token in hands, you will just redirect
 # the user to authenticate at Twitter
 # See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 2)
 redirect TwitterSignIn.authenticate_url(token)
 end

O método request_token está implementado na classe TwitterSignIn:

Listagem 2:

https://github.com/lfcipriani/sign_in_with_twitter_sample/blob/master/lib/twitter_sign_in.rb#L15

def request_token
# The request to get request tokens should only
 # use consumer key and consumer secret, no token
 # is necessary
 response = TwitterSignIn.request(
 :post,
 "https://api.twitter.com/oauth/request_token",
 {},
 @oauth
 )
obj = {}
 vars = response.body.split("&").each do |v|
 obj[v.split("=").first] = v.split("=").last
 end
# oauth_token and oauth_token_secret should
 # be stored in a database and will be used
 # to retrieve user access tokens in next requests
 db = Daybreak::DB.new DATABASE
 db.lock { db[obj["oauth_token"]] = obj }
 db.close
return obj["oauth_token"]
 end

Basicamente, essa requisição feita à API do Twitter irá retornar dois valores: oauth_token e oauth_token_secret. Essa será sua credencial temporária fornecida pelo Twitter para que você consiga requisitar um token de acesso à conta do usuário.

Passo 2: Redirecionando o usuário

Com essa credencial em mãos, você pode redirecionar o usuário para /oauth/authenticate passando o oauth_token obtido no passo anterior.

Listagem 3:

https://github.com/lfcipriani/sign_in_with_twitter_sample/blob/master/lib/twitter_sign_in.rb#L43

def authenticate_url(query)
 # The redirection need to be done with oauth_token
 # obtained in request_token request
 "https://api.twitter.com/oauth/authenticate?oauth_token=" + query
 end

Nesse momento, o usuário será redirecionado para o Twitter e será perguntado se ele deseja liberar o acesso da sua aplicação para acessar os seus recursos. Se o usuário autorizar o acesso, o Twitter vai redirecioná-lo de volta para a URL de callback que você configurou na sua aplicação registrada em apps.twitter.com.

Listagem 4:

https://github.com/lfcipriani/sign_in_with_twitter_sample/blob/master/app.rb#L60

# This callback will be called by user browser after
 # being redirect by Twitter with successful authentication
 # See https://dev.twitter.com/docs/auth/implementing-sign-twitter (end of Step 2)
 get '/callback' do
# Given that the user authorized us, we now
 # need to get its Access Token.
 # See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 3)
 token = TwitterSignIn.access_token(params["oauth_token"], params["oauth_verifier"])

A redireção possui um parâmetro chamado oauth_verifier, que é a confirmação que o usuário autorizou a sua aplicação. O parâmetro oauth_token também retornado permite você identificar qual usuário você está autenticando e você deve verificar se ele bate com o valor que foi retornado no primeiro passo.

Passo 3: Converter o request token em um access token

Agora você irá utilizar todos os tokens obtidos em passos anteriores para requisitar o token de acesso. É com o token de acesso que você irá, assim como o nome sugere, acessar o Twitter em nome do usuário que acabou de te autorizar. Vamos dar uma olhada na implementação desse passo.

Listagen 5:

https://github.com/lfcipriani/sign_in_with_twitter_sample/blob/master/lib/twitter_sign_in.rb#L49

# See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 3)
 def access_token(oauth_token, oauth_verifier)
# To request access token, you need to retrieve
 # oauth_token and oauth_token_secret stored in
 # database
 db = Daybreak::DB.new DATABASE
 if dbtoken = db[oauth_token]
# now the oauth signature variables should be
 # your app consumer keys and secrets and also
 # token key and token secret obtained in request_token
 oauth = @oauth.dup
 oauth[:token] = oauth_token
 oauth[:token_secret] = dbtoken["oauth_token_secret"]
# oauth_verifier got in callback must
 # to be passed as body param
 response = TwitterSignIn.request(
 :post,
 "https://api.twitter.com/oauth/access_token",
 {:oauth_verifier => oauth_verifier},
 oauth
 )
obj = {}
 vars = response.body.split("&").each do |v|
 obj[v.split("=").first] = v.split("=").last
 end
# now the we got the access tokens, store it safely
 # in database, you're going to use it later to
 # access Twitter API in behalf of logged user
 dbtoken["access_token"] = obj["oauth_token"]
 dbtoken["access_token_secret"] = obj["oauth_token_secret"]
 db.lock { db[oauth_token] = dbtoken }
else
 oauth_token = nil
 end
db.close
 return oauth_token
 end

O método TwitterSignIn.access_token realiza uma requisição para POST /oauth/access_token passando a consumer_key e secret, bem como oauth_token e oauth_token_secret dentro da assinatura de OAuth; já o oauth_verifier, obtido no passo 2, será passado no corpo da requisição.

Com esse conjunto de informação, o Twitter pode validar que essa requisição de um token de acesso é legítima e vai retornar os tokens de acesso do usuário. Dessa forma você agora poderá realizar requisições à API do Twitter em nome desse usuário. Você deve também armazenar esses tokens de acesso de maneira segura na sua base de dados.

Passo 4: Usando o token de acesso

Essa aplicação de exemplo implementa uma funcionalidade que permite que você siga uma conta configurada previamente. Isso é apenas um exemplo do que pode ser feito uma vez que você obtém autorização do usuário.

Listagem 6:

https://github.com/lfcipriani/sign_in_with_twitter_sample/blob/master/app.rb#L118

# Building oauth signature vars to use in this request
 # Basically, it's our app consumer vars combined
 # with user access tokens
 oauth = @oauth.dup
 oauth[:token] = dbtoken["access_token"]
 oauth[:token_secret] = dbtoken["access_token_secret"]
# A POST request in https://dev.twitter.com/docs/api/1.1/post/friendships/create
 # to make the logged user follow the ACCOUNT_TO_FOLLOW
 response = TwitterSignIn.request(
 :post,
 "https://api.twitter.com/1.1/friendships/create.json",
 {:screen_name => ACCOUNT_TO_FOLLOW},
 oauth
 )

Conclusão

Agora que você já sabe como implementar o login social com o Twitter, dê uma olhada também no código completo deste exemplo para ter uma visão geral de como a aplicação funciona. Se você é um pythonista, dê uma olhada na implementação em Python feita pelo @jaakkosf.

Da mesma forma, se você já construiu uma integração que implementa o login social com o Twitter e ela pode ser extraída para uma aplicação stand-alone, compartilhe com a gente tweetando o link para a conta @TwitterDevBr. Nós iremos atualizar esse artigo a medida que mais exemplos de linguagens forem compartilhadas.