Google :: Apis :: AuthorizationError (неавторизованный)

Мы создаем приложение с Ionic framework как front-end и Ruby on Rails в качестве back-end. Мы можем связать учетную запись Gmail в нашем приложении. Учетная запись работает нормально, мы получаем serverAuthCode из front-end, а затем используем то, что получаем токен обновления, и мы можем при первой попытке получать электронные письма с этим токеном обновления. Но через несколько секунд он истекает или отменяется. Получение следующей проблемы:

Signet::AuthorizationError (Authorization failed.  Server message:
{
  "error" : "invalid_grant",
  "error_description" : "Token has been expired or revoked."
})

Кажется, обновленный токен истекает через секунды. Кто-нибудь знает, как это исправить?

Обновить:

Существующий код выглядит следующим образом:

class User   
  def authentication(linked_account)
    client = Signet::OAuth2::Client.new(
    authorization_uri: 'https://accounts.google.com/o/oauth2/auth',
    token_credential_uri: Rails.application.secrets.token_credential_uri,
    client_id: Rails.application.secrets.google_client_id,
    client_secret: Rails.application.secrets.google_client_secret,
    scope: 'https://www.googleapis.com/auth/gmail.readonly, https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile',
    redirect_uri: Rails.application.secrets.redirect_uri,
    refresh_token: linked_account[:refresh_token]
  )

  client.update!(access_token: linked_account.token, expires_at: linked_account.expires_at)
  return  AccessToken.new(linked_account.token) unless client.expired?
  auth.fetch_access_token! 
 end

 def get_email(linked_account)
   auth = authentication(linked_account)
   gmail = Google::Apis::GmailV1::GmailService.new
   gmail.client_options.application_name = User::APPLICATION_NAME
   gmail.authorization = AccessToken.new(linked_account.token)
   query = "(is:inbox OR is:sent)"
   gmail.list_user_messages(linked_account[:uid], q: "#{query}")
   ## Getting error over here ^^
  end
end // class end 

class AccessToken
  attr_reader :token
  def initialize(token)
    @token = token
  end

  def apply!(headers)
    headers['Authorization'] = "Bearer #{@token}"
  end
end

Ссылка ссылки: https://github.com/google/google-api-ruby-client/issues/296

Ответы

Ответ 1

Мысль о том, что refresh token никогда не истекает, на самом деле является недоразумением. Фактическая сцена заключается в том, что сервер выдает недолгосрочный токен доступа и долгоживущий токен обновления. Таким образом, на самом деле происходит то, что токен доступа может быть восстановлен с использованием долгоживущих токенов обновления, но да, вам придется запросить новый токен обновления (по мере того, как он истек!). Например; вы можете обрабатывать токены обновления, как будто они никогда не истекают. Однако при входе в систему проверить новый, если пользователь отменит токен обновления, в этом случае Google предоставит новый токен обновления при входе в систему, чтобы просто обновить токен обновления.

Теперь условие может состоять в том, что пользователь отменяет доступ к вашему приложению. В этом случае refresh token истекает (или я должен сказать, что он станет несанкционированным). Так что если это сценарий в вашем случае, вам придется подумать об отказе от отзыва доступа для приложения.

Чтобы лучше понять это, вы можете обратиться к этому документу и даже к документации OAuth 2.0.

Ответ 2

Из того, что я могу догадаться, проблема, похоже, заключается в этих двух строках. Проверяется истечение срока действия маркера и генерируется новый токен. Было бы замечательно, если бы был минимальный воспроизводимый код.

return  AccessToken.new(linked_account.token) unless client.expired?
auth.fetch_access_token! 

Вот как я получаю свой токен доступа:

  def self.access_token(refresh_token)
    Cache.fetch(refresh_token, expires_in: 60.minutes) do
      url = GoogleService::TOKEN_CREDENTIAL_URI
      # p.s. TOKEN_CREDENTIAL_URI = 'https://www.googleapis.com/oauth2/v4/token'
      _, response = Request.post(
        url,
        payload: {
          "client_id": GoogleService::CLIENT_ID,
          "client_secret": GoogleService::CLIENT_SECRET,
          "refresh_token": refresh_token,
          "grant_type": "refresh_token"
        }
      )
      response['access_token']
    end
  end

И затем используйте этот токен доступа для любых целей. Дайте мне знать, как это происходит, а также если вы можете создать воспроизводимую версию API. Что будет здорово.

Ответ 3

Существует несколько причин, по которым токен обновления перестает работать.

  1. Он доходит до старых токенов обновления, срок действия которых истекает через шесть месяцев, если не используется.
  2. Пользователь может повторно указать ваше приложение и получить новый токен обновления, а токены обновления будут работать, вы можете иметь максимум пятьдесят выдающихся токенов обновления, после чего первый перестанет работать.
  3. пользователь может отменить ваш доступ.
  4. Wakey летнее время ошибка 2015. (мы не будем говорить об этом)

Gmail и сбросить пароль.

В основном это связано с сбросом пароля. Предоставление OAuth с областями gmail отменяется, когда пользователь меняет свой пароль.

См. Автоматическое отмену токена OAuth 2.0 при смене пароля

В общем, пользователи могут аннулировать гранты в любое время. Вы должны уметь обрабатывать этот случай изящно и предупреждать пользователя о необходимости повторной авторизации, если они хотят продолжать использовать предоставленные функции.

Вы проводите много тестов, я бы предположил, что вы сохраняете новый токен обновления? Если нет, то вы можете использовать старые токены обновления, и они перестанут работать. (номер 2)

Ответ 4

Вы пытались обновить токен доступа с помощью токена обновления? Вы можете поймать ошибку и повторить попытку.

Что-то вроде этого:

begin
  gmail.list_user_messages(linked_account[:uid], q: "#{query}")
rescue Google::Apis::AuthorizationError => exception
  client.refresh!
  retry
end

Ответ 5

Недостаточно кода отправлено, но то, что опубликовано, выглядит неправильно.

  • linked_account не определен
  • Нигде не показано, что linked_account.token когда-либо обновляется (или задается, если на то пошло). Он должен быть обновлен, когда refresh_token используется для получения нового токена доступа.
  • auth отображается в строке auth.fetch_access_token!
  • GmailService#authorization= принимает Signet::OAuth2::Client не AccessToken.

Вероятно, что происходит, так это то, что у вас есть действительный токен доступа в linked_account.token пока вы не client.update! , который извлекает новый токен доступа и делает недействительным старый. Но поскольку вы никогда не обновляете linked_account, будущие вызовы терпят неудачу, пока вы не linked_account путь кода, который сбрасывает его.

Вам нужно только позвонить client.update! если токен доступа истек, и если он истек, и вы получили новый, вам нужно сохранить этот новый в linked_account.token.