2

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.

Edgars
  • 913
  • 1
  • 19
  • 53

1 Answers1

1

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.

Eugene Primako
  • 2,767
  • 9
  • 26
  • 35
  • I have tried in Chrome Postman and I receive the same response as in my Rails app and it is "Code has expired". – Edgars Apr 11 '17 at 11:42
  • 1
    And what client_id do you use when you get authorization_code? Is it equal to `Rails.application.secrets.client_id`? – Eugene Primako Apr 11 '17 at 12:03
  • Yes, I used the same client_id. if I changed few symbols(for debuging) request returned cannot find such client or client doesn't exist. Can't remember exactly.. – Edgars Apr 11 '17 at 12:37