HTTPBadRequest {“error_description”:“代码已过期”,“错误”:“invalid_grant”}用于Oauth身份验证,Ruby on Rails

时间:2017-04-04 09:19:26

标签: ruby-on-rails ruby oauth omniauth yandex-api

我的omniauth应用程序有奇怪的行为。基本上,我有管理员面板,需要访问它才能使用Yandex帐户进行身份验证。

问题:我在多个指南中按要求执行了所有操作,一切正常,因为昨天我尝试使用Yandex帐户进行身份验证,并收到了HTTPBadRequest错误。

注意:我的代码中没有改变。我的所有访问数据client_Id和密码都没有改变。

的Gemfile:

    package com.example.runnable;

import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import static com.example.runnable.R.id.map;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

    private GoogleMap mMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);



    }


    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        // Add a marker in Sydney and move the camera
        mMap.addMarker(new MarkerOptions().position(new LatLng(54.581765, -5.937637)).title("Marker"));



        // Enabling MyLocation Layer of Google Map
        googleMap.setMyLocationEnabled(true);

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        mMap.setMyLocationEnabled(true);
        mMap.getUiSettings().setMyLocationButtonEnabled(true);

    }

}

路线:

gem "omniauth-yandex"

CallbacksController:

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

用户模型:

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

问题

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 错误突出显示HTTPBadRequest中的行:

CallbacksController

我尝试了什么

1)重新启动Rails app;

2)从数据库中删除用户并尝试再次登录;

3)删除了 Yandex Oauth 部分中的注册应用。然后使用新的client_id和密码再次添加回来。

来自Yandex Oauth指南

@response_mess = http.request(req)

更新:我收到此错误:

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>]

更新2 :我试图联系Yandex支持。我把简短的描述发给我的问题并链接到问题。但没有回应。

更新3:

我在CHROME POSTMAN中尝试了相同的POST请求并收到与

相同的响应
  

{“error_description”:“代码已过期”,“错误”:“invalid_grant”}

更新4:

我再次检查了Yandex的所有凭据client_id和密码,它们100%有效。

1 个答案:

答案 0 :(得分:3)

来自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.

Yandex site

进一步说明了这一点
  

无法发出access_token。临时授权代码不是由Yandex.Money发布的,或者已经过期,或者已经为此临时授权代码发出了access_token(使用相同的临时授权代码对访问令牌的重复请求)。

您正在向Yandex提供无效凭据。忘记你刚才写的代码,因为唯一失败的部分是它将数据发送到Yandex。 Yandex告诉你数据无效,你已经向Postman证实了这一点。这意味着这将发送无效数据:

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

仔细检查这些事情:

  1. grant_type应该是authorization_code吗?
  2. 您确定@authorization_code是否设置为值(而不是nil)且值是否有效?
  3. @client_id的相同问题?
  4. 同样的问题@secret
  5. 您错过了redirect_uri值吗?