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:
grant_type
supposed to be authorization_code
? @authorization_code
is set to a value (and not nil) and that the value is valid? @client_id
? @secret
? 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.