I followed this guide to implement Yandex Oauth in my Ruby On Rails app. Few weeks I had no problems with it, but recently (few days ago) I am experiencing problem. I can't get access token, refresh token and etc. because my request fails with 400 error code.
I am receiving this specific message:
{"error_description": "Code has expired", "error": "invalid_grant"}
From guide:
The lifespan of this code is 10 minutes. When it expires, a code must be requested again.
But it never waits 10 minutes or even 10 seconds because as soon as my app gets authorization code it immediately makes POST request to change this authorization code for access token, refresh token and expiration. But this POST request fails because that authorization code seems to be expired.
From Yandex error descriptions:
invalid_grant ― Invalid or expired authorization code.
My code:
def yandex
require 'net/http'
require 'json' # => false
@user = User.from_omniauth(request.env["omniauth.auth"])
@client_id = Rails.application.secrets.client_id
@secret = Rails.application.secrets.password
@authorization_code = params[:code]
@user.update_attribute(:code, @authorization_code)
@user.update_attribute(:state, params[:state])
@post_body = "grant_type=authorization_code&code=#{@authorization_code}&client_id=#{@client_id}&client_secret=#{@secret}"
@url = "https://oauth.yandex.ru/token"
url = URI.parse(@url)
req = Net::HTTP::Post.new(url.request_uri)
req['host'] ="oauth.yandex.ru"
req['Content-Length'] = @post_body.length
req['Content-Type'] = 'application/x-www-form-urlencoded'
req.body = @post_body
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = (url.scheme == "https")
@response_mess = http.request(req)
refreshhash = JSON.parse(@response_mess.body)
access_token = refreshhash['access_token']
refresh_token = refreshhash['refresh_token']
access_token_expires_at = DateTime.now + refreshhash["expires_in"].to_i.seconds
if access_token.present? && refresh_token.present? && access_token_expires_at.present?
@user.update_attribute(:access_token, access_token)
@user.update_attribute(:refresh_token, refresh_token)
@user.update_attribute(:expires_in, access_token_expires_at)
sign_in(@user)
redirect_to admin_dashboard_index_path
end
end
My log:
Started GET "/users/auth/yandex/callback?state=31c11a86beecdeb55ab30887d56391a8cbafccdc85c456aa&code=5842278" for 212.3.192.116 at 2017-04-05 15:58:27 +0300
Cannot render console from 212.3.192.116! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by CallbacksController#yandex as HTML
Parameters: {"state"=>"31c11a86beecdeb55ab30887d56391a8cbafccdc85c456aa", "code"=>"5842278"}
I tried the same POST request in CHROME POSTMAN and received the same response as {"error_description": "Code has expired", "error": "invalid_grant"}
I have googled around but there isn't any similar question.. It seems that the problem is at Yandex end, but how get around it ?
Any help will be appreciated.
Yandex authorization codes are single-use. Are you sure you are not making request to "https://oauth.yandex.ru/token" twice? You can try making such a request in Chrome Postman before evaluating your code that does the same.