ruby on rails - Caducidad y renovación de token de Facebook, con Koala y omniauth-facebook
ruby-on-rails (3)
Estoy escribiendo una aplicación de Rails que usa omniauth-facebook para autenticar al usuario contra FB (y para obtener un token de acceso FB OAuth para el usuario). La aplicación luego utiliza Koala para realizar varias llamadas a la API de FB Graph, utilizando el token OAuth guardado.
Actualizo el token guardado cada vez que el usuario vuelve a autenticarse (generalmente cuando inicia sesión en mi aplicación). Aun así, el token guardado caducará (o se volverá inválido) de vez en cuando.
¿Cuál es la mejor práctica para protegerse contra fallas de autenticación y actualizar el token mientras usa Koala?
¿Todas las llamadas deben estar envueltas en bloques de inicio / rescate, con un controlador de excepciones que vuelva a autenticar al usuario contra FB?
¿Hay alguna forma (usando Koala) de aprovechar el proceso de ''extensión de tokens de acceso'' descrito here ? Si no, ¿existen mejores prácticas para escribir mi propio código para extraer el token nuevo de una llamada Koala?
Lo que tengo es un before_filter que se activa en cada página que requiere una sesión activa de Facebook. Algo como esto debería funcionar:
before_filter :reconnect_with_facebook
def reconnect_with_facebook
if current_account && current_account.token_expired?(session[:fb]["expires"])
# re-request a token from facebook. Assume that we got a new token so
# update it anyhow...
session[:return_to] = request.env["REQUEST_URI"] unless request.env["REQUEST_URI"] == facebook_request_path
redirect_to(with_canvas(facebook_request_path)) and return false
end
end
El token_expirado? El método se ve así:
def token_expired?(new_time = nil)
expiry = (new_time.nil? ? token_expires_at : Time.at(new_time))
return true if expiry < Time.now ## expired token, so we should quickly return
token_expires_at = expiry
save if changed?
false # token not expired. :D
end
Me encontré con esta publicación que adapta el código de Railscast en Facebook para mostrar cómo se puede intercambiar el token de corta duración por el de 60 días:
usuario.rb
def self.from_omniauth(auth)
# immediately get 60 day auth token
oauth = Koala::Facebook::OAuth.new(ENV["FACEBOOK_APP_ID"], ENV["FACEBOOK_SECRET"])
new_access_info = oauth.exchange_access_token_info auth.credentials.token
new_access_token = new_access_info["access_token"]
# Facebook updated expired attribute
new_access_expires_at = DateTime.now + new_access_info["expires_in"].to_i.seconds
where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.image = auth.info.image
user.email = auth.info.email
user.oauth_token = new_access_token #originally auth.credentials.token
user.oauth_expires_at = new_access_expires_at #originally Time.at(auth.credentials.expires_at)
user.save!
end
end
Puedes hacer algo como esto en el que verificas si el access_token está vencido y genera otro.
%w[facebook].each do |provider|
scope provider, -> { where(provider: provider) }
end
def client
send("#{provider}_client")
end
def expired?
expires_at? && expires_at <= Time.zone.now
end
def access_token
send("#{provider}_refresh_token!", super) if expired?
super
end
def facebook_refresh_token!(token)
new_token_info =
Koala::Facebook::OAuth.new.exchange_access_token_info(token)
update(access_token: new_token_info["access_token"], expires_at: Time.zone.now + new_token_info["expires_in"])
end
Puede consultar los videos de gorailes que explican esto en profundidad.