HTTPBadRequest {"error_description": "Code expired", "error": "invalid_grant"} for Oauth authentication, Ruby on Rails

I have strange behavior in my omniauth app. Basically, I have an admin panel that requires authentication with a Yandex account.

Problem . I did everything that was required in several tutorials and everything went fine since yesterday I was trying to authenticate with a Yandex account and I got an HTTPBadRequest error.

Note . I haven't changed the code in my code. All my client_Id access details and password have not 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

the error highlights the line in CallbacksController

:

@response_mess = http.request(req)

      

What I have tried :

1) Restarted Rails application;

2) Removed user from database and tried to login again;

3) Removed registered application in the Yandex Oauth section . Then added a new client_id and password again.

From Yandex Oauth manual :

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 getting this error:

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

      

UPDATE 2 . I tried to contact Yandex support. I sent them a short description of my problem and a link to the question. But there is no answer.

UPDATE 3:

I tried the same POST request on CHROME POSTMAN and got the same response as

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

Update 4:

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

+3


source to share


1 answer


From 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 explained further on the Yandex website :

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



You are sending invalid credentials to Yandex. Forget the code you wrote for a moment, because the only faulty part is when it sends data to Yandex. Yandex informs that the data is invalid, and you confirmed it using Postman. This means that this is sending the wrong data:

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

      

So, double check these things:

  • Is it supposed grant_type

    authorization_code

    ?
  • Are you sure you are @authorization_code

    set to a value (and not zero) and that this value is valid?
  • Same question for @client_id

    ?
  • The same question @secret

    ?
  • Are you missing the meaning redirect_uri

    ?
+3


source







All Articles