Rails 4 - 使用omniauth-google-oauth2的Google One Time Code Flow,继续获取"无效代码"

时间:2015-06-18 18:04:46

标签: ruby-on-rails ruby oauth-2.0 google-api google-oauth

我有一个Rails应用,我希望用户能够使用Google登录/注册。我使用以下gem:

#gemfile
gem 'omniauth-google-oauth2'

我几乎让它工作了(我实际上收到了一次访问令牌,不知道为什么) - 但在获取access_token之前我经常遇到以下错误:

"error"=>"invalid_grant", "error_description"=>"Invalid code."

我已经检查过,因此代码对于每个请求都是唯一的,并且它不是。在我收到一次授权代码后尝试获取access_token的方法的相关部分如下所示:

def google_authentication
  respond_to do |format|
    # authorisation code

    code = params[:code]
    unless code.blank?

      client_id = ENV["GOOGLE_CLIENT_ID"]
      client_secret = ENV["GOOGLE_CLIENT_SECRET"]
      redirect_uri = 'postmessage'
      grant_type = 'authorization_code'

      load = {client_id: client_id, client_secret: client_secret,  redirect_uri: redirect_uri, grant_type: grant_type, code: code}
      payload = load.to_json
      url = "https://www.googleapis.com/oauth2/v3/token"
      response = HTTParty.post(url, :query => load)

      json = JSON.parse(response.body)

      unless json.nil?
        unless json[:error].present?
          # TODO: Handle data      
          format.json { render :json => {:message => "Success"} }              
        else
          # ERROR "Invalid code" always happen
        end
      end
    end
  end
end

在Google的开发者控制台中,我有以下凭据:

Client ID           [CLient ID]
Email address       [Email]
Client secret       [Secret]
Redirect URIs       http://127.0.0.1:3000/
JavaScript origins  http://127.0.0.1:3000

感谢任何想法或提示。

完成更新

这就是我设置omniauth-google-oauth2:

的方法
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"],  ENV["GOOGLE_CLIENT_SECRET"],
    {
      :scope => "email, profile",
      :prompt => "select_account",
      :provider_ignores_state => true
    }
end

更新2

如上所述,我曾经设法获得访问令牌一次,我设法再次重现它。我是通过点击我的登录按钮三次来完成的。我第一次得到:

"error"=>"invalid_grant", "error_description"=>"Invalid code."

第二次点击导致:

"error"=>"invalid_grant", "error_description"=>"Code was already redeemed."

第三次成功获得access_token。我觉得很奇怪我有时会得到access_token,但大部分时间都会得到:

"error"=>"invalid_grant", "error_description"=>"Invalid code."

成功/错误"率"并非100%一致。有时需要超过三次点击。我觉得很奇怪,它有时会起作用,我有时会得到不同的错误响应而不会改变我的代码中的任何内容。

它可能与代码的时间/到期日期有关吗?

更新3

进一步完成。这是我的Javascript(或Cof​​feeScript)在用户点击Google登录按钮时的样子:

$(document).ready ->
  $.ajax
    url: 'https://apis.google.com/js/client:plus.js?onload=gpAsyncInit'
    dataType: 'script'
    cache: true


window.gpAsyncInit = ->
  $('.googleplus-login').click (e) ->
    e.preventDefault()
    gapi.auth.authorize {
      immediate: false
      response_type: 'code'
      cookie_policy: 'single_host_origin'
      client_id: '[id]'
      scope: 'email profile'
    }, (response) ->
      if response and !response.error

        jQuery.ajax
          type: 'POST'
          url: '/auth/google_oauth2/callback'
          dataType: 'json'
          data: response
          success: (json) ->
            # response from server
            console.log "JSON: " + json
            return
      else
        # google authentication failed

2 个答案:

答案 0 :(得分:1)

我无法帮助处理红宝石部分,但我可以帮助你找出错误的原因。

实际上,Google的验证服务器返回了3个代码。授权代码,访问令牌和刷新令牌。

  • 授权码只能使用一次才能获得第一个刷新令牌。
  • 访问令牌用于访问apis上的数据,一小时后过期。
  • 刷新令牌用于在到期时获取新的访问令牌。好,直到用户删除访问权限。

授权码

这是用户点击接受您的应用程序时返回的代码。

示例:

将此URI中的客户端ID,密码和范围更改为您正在使用的URI。然后将其粘贴到浏览器位置栏中。

https://accounts.google.com/o/oauth2/auth?client_id={clientid}.apps.googleusercontent.com&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope={scope}&response_type=code

enter image description here

它会提示您进行身份验证。如果你单击,除了你得到另一个带有长代码的窗口,看起来像这样。

enter image description here

这是身份验证代码,它在生活中的唯一目的是让您使用它来获取访问令牌和刷新令牌。它只能用于一次,虽然我从未测试过它们有多长时间,但它可能很短暂。

在上面的uri中注意:响应类型代码。

交换:

获得该身份验证代码后,您需要将其替换为访问令牌和刷新令牌。这是一个HTTP POST,因此无法放在浏览器窗口中。

https://accounts.google.com/o/oauth2/token
code=4/X9lG6uWd8-MMJPElWggHZRzyFKtp.QubAT_P-GEwePvB8fYmgkJzntDnaiAI&client_id={ClientId}.apps.googleusercontent.com&client_secret={ClientSecret}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code

注意:grant_type = authorization_code这告诉服务器您要向其发送授权码。

回复

{
"access_token" : "ya29.1.AADtN_VSBMC2Ga2lhxsTKjVQ_ROco8VbD6h01aj4PcKHLm6qvHbNtn-_BIzXMw",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "1/J-3zPA8XR1o_cXebV9sDKn_f5MTqaFhKFxH-3PUPiJ4"
}

您现在拥有一个可用于访问Google API的访问令牌,它的短暂持续时间仅为3600秒或1小时。之后,您必须使用刷新令牌再次访问。

使用refreshtoken

https://accounts.google.com/o/oauth2/token
client_id={ClientId}.apps.googleusercontent.com&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token

<强>响应

{
"access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ",
"token_type" : "Bearer",
"expires_in" : 3600
}

现在您已了解所有这些

"error"=>"invalid_grant", "error_description"=>"Code was already redeemed."

表示您再次发送授权代码,只有在您再次发送刷新令牌后才能使用它。您的身份验证流程有所不足。再说抱歉,我无法帮助红宝石部分。

代码摘自Google 3 legged oauth2 flow

答案 1 :(得分:1)

所以...正如它写的那样:

  

授权重定向   URI每行一个URI。需要一个协议,   没有URL片段,也没有相对路径。无法成为公共IP地址。

您的设置:

  

重定向URI http://127.0.0.1:3000/   JavaScript起源http://127.0.0.1:3000/

......错了。

应该是:

  

重定向URI:http://my.true.domain/users/auth/google/callback

 provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"],  ENV["GOOGLE_CLIENT_SECRET"],
    {
      :scope => "email, profile",
      :name => "google",
      ...

我希望它有所帮助!