0

When calling the HERE authentication service (https://account.api.here.com/oauth2/token) from one of the controllers of the RoR APP (Rails 5.0.6/ruby 2.6.1) I get a 401: "401300 Signature mismatch. Authorization signature or client credential is wrong"

The Key, secret, Authorization header, content type, request body etc ... are the same as the ones used by Postman.

Postman always returns a 200 OK but the rails app systematically returns "401"

Any suggestions on what the problem is?

 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
ZPSFOS
  • 1
  • yes, please check section "C) Create the signing key" of this document: https://developer.here.com/documentation/identity-access-management/dev_guide/topics/sdk.html#step-2-create-a-signature – ZPSFOS Jul 29 '21 at 12:31
  • Double check your key, secret and escaping. – dbugger Jul 29 '21 at 12:49

1 Answers1

0

I don't know RoR but I had the same problem in Javascript and this script solved my problem:

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
  • 104
  • 5
  • Thanks @Erik Lima. I got this working on vanilla JS as well (and on erb in one of the app views as well) but this does not solve the problem. I need this to work from within one of the app controllers. I just don't understand what I'm doing wrong. I looked at the SHA256/Base64 strings all seem ok ... any other suggestion? – ZPSFOS Aug 16 '21 at 15:49