web-dev-qa-db-fra.com

Actualiser le jeton à l'aide de l'application Omniauth-oauth2 dans Rails

J'utilise omniauth-oauth2 dans Rails pour m'authentifier sur un site prenant en charge oauth2. Après avoir fait la danse oauth, le site me donne ce qui suit, que je persiste dans la base de données:

  1. Jeton d'accès
  2. Expires_AT (ticks)
  3. Actualiser le jeton

Existe-t-il une méthode omniauth pour actualiser automatiquement le jeton après son expiration ou devrais-je écrire un code personnalisé pour faire de même? 

Si un code personnalisé doit être écrit, un assistant est-il le bon endroit pour écrire la logique?

25
ganeshran

Omniauth n'offrant pas cette fonctionnalité immédiatement, j'ai utilisé la réponse précédente et une autre réponse SO pour écrire le code dans mon modèle User.rb

def refresh_token_if_expired
  if token_expired?
    response    = RestClient.post "#{ENV['DOMAIN']}oauth2/token", :grant_type => 'refresh_token', :refresh_token => self.refresh_token, :client_id => ENV['APP_ID'], :client_secret => ENV['APP_SECRET'] 
    refreshhash = JSON.parse(response.body)

    token_will_change!
    expiresat_will_change!

    self.token     = refreshhash['access_token']
    self.expiresat = DateTime.now + refreshhash["expires_in"].to_i.seconds

    self.save
    puts 'Saved'
  end
end

def token_expired?
  expiry = Time.at(self.expiresat) 
  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

Et avant de passer l'appel d'API à l'aide du jeton d'accès, vous pouvez appeler la méthode de cette manière, où utilisateur_diffusé est l'utilisateur connecté.

current_user.refresh_token_if_expired

Veillez à installer le fichier rest-client gem et à ajouter la directive require require 'rest-client' dans le fichier de modèle. ENV['DOMAIN'], ENV['APP_ID'] et ENV['APP_SECRET'] sont des variables d'environnement pouvant être définies dans config/environments/production.rb (ou développement)

19
ganeshran

En fait, la gem omniauth-oauth2 et sa dépendance, oauth2 , intègrent toutes les deux une logique de rafraîchissement.

Voir dans https://github.com/intridea/oauth2/blob/master/lib/oauth2/access_token.rb#L80

# Refreshes the current Access Token
#
# @return [AccessToken] a new AccessToken
# @note options should be carried over to the new AccessToken
def refresh!(params = {})
  fail('A refresh_token is not available') unless refresh_token
  params.merge!(:client_id      => @client.id,
                :client_secret  => @client.secret,
                :grant_type     => 'refresh_token',
                :refresh_token  => refresh_token)
  new_token = @client.get_token(params)
  new_token.options = options
  new_token.refresh_token = refresh_token unless new_token.refresh_token
  new_token
end

Et dans https://github.com/intridea/omniauth-oauth2/blob/master/lib/omniauth/strategies/oauth2.rb#L74 :

self.access_token = access_token.refresh! if access_token.expired?

Donc, vous ne pourrez peut-être pas le faire directement avec omniauth-oauth2, mais vous pouvez certainement faire quelque chose dans le sens de ceci avec oauth2:

client = strategy.client # from your omniauth oauth2 strategy
token = OAuth2::AccessToken.from_hash client, record.to_hash
# or
token = OAuth2::AccessToken.new client, token, {expires_at: 123456789, refresh_token: "123"}
token.refresh!
17
Eero

La réponse d'Eero m'a ouvert un chemin pour résoudre ce problème. J'ai un problème d'assistance pour mes cours qui me permet d'obtenir un service Gmail. Dans le cadre de ce processus, l'objet utilisateur (qui contient les informations d'authentification de Google) est vérifié s'il a expiré. Si tel est le cas, il est actualisé avant de renvoyer le service.

def gmail_service(user)
  mail = Google::Apis::GmailV1::GmailService.new

  # Is the users token expired?
  if user.google_token_expire.to_datetime.past?
    oauth = OmniAuth::Strategies::GoogleOauth2.new(
      nil, # App - nil seems to be ok?!
      "XXXXXXXXXX.apps.googleusercontent.com", # Client ID
      "ABC123456" # Client Secret
    )
    token = OAuth2::AccessToken.new(
      oauth.client,
      user.google_access_token,
      { refresh_token: user.google_refresh_token }
    )
    new_token = token.refresh!

    if new_token.present?
      user.update(
        google_access_token: new_token.token,
        google_token_expire: Time.at(new_token.expires_at),
        google_refresh_token: new_token.refresh_token
      )
    else
      puts("DAMN - DIDN'T WORK!")
    end
  end

  mail.authorization = user.google_access_token

  mail
end
6
Nick

Il y a quelques informations ici, trop à énumérer ici . Cela peut dépendre du fournisseur que vous utilisez et de l’utilisation autorisée du refresh-token

5
Shane O'Grady

De la même manière que d’autres réponses, j’ai suivi cette approche, où le modèle stockant l’authentification et les jetons d’actualisation est utilisé, faisant abstraction des interactions entre API et cette logique.

Voir https://stackoverflow.com/a/51041855/1392282

0
Nuno Silva