【问题标题】:Why does HERE oauth2 token request api return 401300 with Rails but works fine with Postman?为什么 HERE oauth2 令牌请求 api 使用 Rails 返回 401300,但使用 Postman 可以正常工作?
【发布时间】:2021-10-05 01:20:26
【问题描述】:

从 RoR APP(Rails 5.0.6/ruby 2.6.1)的控制器之一调用 HERE 身份验证服务 (https://account.api.here.com/oauth2/token) 时,我收到 401:“401300 签名不匹配。授权签名或客户端凭据错误"

Key、secret、Authorization header、content type、request body等……和Postman使用的一样。

Postman 总是返回 200 OK,但 rails 应用程序系统地返回“401”

对问题所在有什么建议吗?

 def fetch_new_token
    # URL
    api_url = 'https://account.api.here.com/oauth2/token'
    # VERSION        
    api_version='1.0'
    # GRANT TYPE
    api_grant_type_for_req_body='grant_type=client_credentials'
    #KEY
    api_access_key_id = CGI.escape(ENV['my_access_key_id'])
    #SECRET
    api_access_key_secret = CGI.escape(ENV['my_access_key_secret'])
    #NONCE
    draft_api_nonce= [('a'..'z'), ('A'..'Z')].map(&:to_a).flatten
    api_nonce=(0...20).map { draft_api_nonce[rand(draft_api_nonce.length)] }.join
    #TMESTAMP
    api_timestamp = (Time.now).strftime('%s') 
    #NORMALIZED URL
    api_url_normalized = CGI.escape(api_url)
    #SIGNING METHOD
    api_signature_method= CGI.escape('HMAC-SHA256')
    #OAUTH PARAMETERS BASE STRING
    api_parameters_string=('consumer_key='+api_access_key_id+'&nonce='+api_nonce+'&signature_method='+api_signature_method+'&timestamp='+api_timestamp+'&'+'version=1.0')
    #ENCODED BASE STRING
    api_normalized_string = 'POST&'+api_url_normalized+'&'+api_grant_type_for_req_body+CGI.escape('&'+api_parameters_string)
    #SIGNNG KEY
    api_signing_key = api_access_key_secret+'&'        
    #SIGNATURE
    digest = OpenSSL::Digest.new('sha256')
    api_signature = OpenSSL::HMAC.hexdigest(digest, api_normalized_string, api_signing_key)
    # convert the HASHING result to a URL ENCODED base64 string.
    api_signature_encoded = (Base64.strict_encode64(api_signature))
    # AUTHORIZATION STRING - ESCAPED
    api_authorization_string = ('OAuth consumer_key="'+api_access_key_id+'",signature_method="'+api_signature_method+'",timestamp="'+CGI.escape(api_timestamp)+'",nonce="'+CGI.escape(api_nonce)+'",version="'+CGI.escape(api_version)+'",signature="'+CGI.escape(api_signature_encoded)+'"')
    # FARADAY OBJECT
    connect_token_request = Faraday.new(url: 'https://account.api.here.com') do |faraday|
        faraday.response :logger, nil, bodies: true
        faraday.request :json
        faraday.headers['Accept'] = 'application/json'
        faraday.headers['Content-Type'] = 'application/x-www-form-urlencoded'
        faraday.headers['Authorization'] = api_authorization_string
        faraday.adapter Faraday.default_adapter
    end
    # FARADAY POST         
    response_token_request= connect_token_request.post('/oauth2/token', 'grant_type=client_credentials' )
    # CHECK THE RESULT  
    puts response_token_request.body 
           
    @json = JSON.parse(response_token_request.body)
    req_status = @json['httpStatus']
    
    puts "The status returned in the body is:::: #{req_status}"
    puts "===== ///// ======"
    puts "===== ///// ======"
    req_error_code = @json['errorCode']
    
    puts "The ERROR CODE returned in the body is:::: #{req_error_code}"
    
end

【问题讨论】:

标签: ruby-on-rails base64 here-api sha256 hmac


【解决方案1】:

我不知道 RoR,但我在 Javascript 中遇到了同样的问题,这个脚本解决了我的问题:

const axios = require('axios')
const cryptoJS = require('crypto-js');
const btoa = require('btoa');

exports.getToken = (app_key, app_secret) => {
    let url = "https://account.api.here.com/oauth2/token";
    let key = encodeURI(app_key);
    let secret = encodeURI(app_secret);
    let nonce = btoa(Math.random().toString(36)).substring(2, 13);
    let timestamp = Math.floor(Date.now()/1000);
    let normalizedUrl = encodeURIComponent(url);
    let signing_method = encodeURI("HMAC-SHA256");
    let sig_string = "oauth_consumer_key="
        .concat(key)
        .concat("&oauth_nonce=")
        .concat(nonce)
        .concat("&oauth_signature_method=")
        .concat(signing_method)
        .concat("&oauth_timestamp=")
        .concat(timestamp)
        .concat("&").concat("oauth_version=1.0");
    let normalised_string = "POST&".concat(normalizedUrl).concat("&").concat(encodeURIComponent(sig_string));
    let signingKey = secret.concat("&");
    let digest = cryptoJS.HmacSHA256(normalised_string, signingKey);
    let signature = cryptoJS.enc.Base64.stringify(digest);
    let auth = 'OAuth oauth_consumer_key="'
        .concat(key)
        .concat('",oauth_signature_method="')
        .concat(signing_method)
        .concat('",oauth_signature="')
        .concat(encodeURIComponent(signature))
        .concat('",oauth_timestamp="')
        .concat(timestamp)
        .concat('",oauth_nonce="')
        .concat(nonce)
        .concat('",oauth_version="1.0"')
    return axios({
        method: 'post',
        url: url,
        data: JSON.stringify({grantType: "client_credentials"}),
        headers: {
            'Content-Type': "application/json",
            'Authorization': auth
        }
    });
}

【讨论】:

  • 感谢@Erik Lima。我也在 vanilla JS 上工作(以及在一个应用程序视图中的 erb 上),但这并不能解决问题。我需要这个从应用程序控制器之一中工作。我只是不明白我做错了什么。我查看了 SHA256/Base64 字符串似乎都还可以……还有其他建议吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 2016-04-17
  • 1970-01-01
  • 2020-12-10
  • 2016-06-26
  • 2019-04-27
  • 1970-01-01
相关资源
最近更新 更多