简体   繁体   中英

HTTPBadRequest {“error_description”: “Code has expired”, “error”: “invalid_grant”} for Oauth authentification, Ruby on Rails

I am having weird behavior of my omniauth app. Basically, I have admin panel to whom access it is required to authenticate with Yandex account.

Problem : I did everything as required in multiple guides and everything worked fine since yesterday I tried to authenticate with Yandex account and I received HTTPBadRequest error.

Note : I haven't changed a bit in my code. All my access data client_Id and password hasn't changed either.

Gemfile:

gem "omniauth-yandex"

Routes:

devise_for :users, :controllers => { :omniauth_callbacks => "callbacks" }

CallbacksController:

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

User model:

    require 'rest-client'

      devise :database_authenticatable, :registerable,
          :recoverable, :rememberable, :trackable, :validatable,
          :omniauthable, :omniauth_providers => [:yandex]

      def self.from_omniauth(auth)
          where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
            user.provider = auth.provider
            user.uid = auth.uid
            user.email =  auth.info.email
            user.code = auth.info.code
            user.state = auth.info.state
            user.password = Devise.friendly_token[0,20]
          end
      end

      def refresh_token_if_expired
          if token_expired?
            response    = RestClient.post "https://oauth.yandex.com/token",
            :grant_type => 'refresh_token',
            :refresh_token => self.refresh_token

            refreshhash = JSON.parse(response.body)

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

            self.save

            puts 'Saved'
          end
      end

      def token_expired?          
          expiry = Time.at(self.expires_in)
          logger.debug "#{expiry}"
          return true if expiry < Time.now 
          token_expires_at = expiry
          save if changed?
          false 
      end
  end

Problem :

HTTPBadRequest error highlights the line in CallbacksController :

@response_mess = http.request(req)

What I have tried :

1) Restarted Rails app;

2) Deleted user from database and tried to sign in again;

3) Deleted registered app in Yandex Oauth section. Then added back again with new client_id and password.

From Yandex Oauth guide :

Exchanging an authorization code for a token

The application sends the code, along with its ID and password, in a POST request.
POST /token HTTP/1.1
Host: oauth.yandex.
Content-type: application/x-www-form-urlencoded
Content-Length: <length of request body>
[Authorization: Basic <encoded client_id:client_secret string>]

   grant_type=authorization_code
 & code=<authorization code>
[& client_id=<application ID>]
[& client_secret=<application password>]
[& device_id=<device ID>]
[& device_name=<device name>]

UPDATE : I am receiving this error:

{"error_description": "Code has expired", "error": "invalid_grant"}

UPDATE 2 : I tried to contact Yandex Support. I sent them brief description to my problem and link to question. But no response.

UPDATE 3:

I tried the same POST request in CHROME POSTMAN and received the same response as

{"error_description": "Code has expired", "error": "invalid_grant"}

Update 4:

I checked again all my credentials client_id and password for Yandex and they are 100% valid.

From the OAUTH 2.0 RFC :

invalid_grant
    The provided authorization grant (e.g., authorization
    code, resource owner credentials) or refresh token is
    invalid, expired, revoked, does not match the redirection
    URI used in the authorization request, or was issued to
    another client.

This is further explained on the Yandex site :

The access_token could not be issued. Either the temporary authorization code was not issued by Yandex.Money, or it has expired, or an access_token has already been issued for this temporary authorization code (a duplicate request for an access token using the same temporary authorization code).

You are supplying invalid credentials to Yandex. Forget the code you've written for a moment because the only part that is failing is when it sends the data to Yandex. Yandex is telling you the data is invalid and you have confirmed this with Postman. That means this is sending invalid data:

@post_body = "grant_type=authorization_code&code=#{@authorization_code}&client_id=#{@client_id}&client_secret=#{@secret}"

So double check these things:

  1. Is the grant_type supposed to be authorization_code ?
  2. Are you certain @authorization_code is set to a value (and not nil) and that the value is valid?
  3. Same question for @client_id ?
  4. Same question @secret ?
  5. Are you missing a redirect_uri value?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM