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
Существует несколько причин, по которым токен обновления перестает работать.
- Он доходит до старых токенов обновления, срок действия которых истекает через шесть месяцев, если не используется.
- Пользователь может повторно указать ваше приложение и получить новый токен обновления, а токены обновления будут работать, вы можете иметь максимум пятьдесят выдающихся токенов обновления, после чего первый перестанет работать.
- пользователь может отменить ваш доступ.
- 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
.