I'm trying to auth a Last.fm session and am struggling to sign a request for a session key correctly.
I keep receiving Invalid method signature supplied
However when I md5 hash what i believe the query should consist of outside of JS, I get the same signature. I must be including the wrong data in the string I guess, but can't figure out what.
I know there are a few other questions and i've ran through them all to see what's going wrong here, but I swear it looks right to me.
This is the signing algorithm and Ajax call. I've tried to leave enough sample data too.
// Set elsewhere but hacked into this example:
var last_fm_data = {
'last_token':'TOKEN876234876',
'user': 'bob',
'secret': 'SECRET348264386'
};
// Kick it off.
last_fm_call('auth.getSession', {'token': last_fm_data['last_token']});
// Low level API call, purely builds a POSTable object and calls it.
function last_fm_call(method, data){
// param data - dictionary.
last_fm_data[method] = false;
// Somewhere to put the result after callback.
// Append some static variables
data['api_key'] = "APIKEY1323454";
data['format'] = 'json';
data['method'] = method;
post_data = last_fm_sign(data);
$.ajax({
type: "post",
url: last_url,
data: post_data,
success: function(res){
last_fm_data[method] = res;
console.log(res['key'])// Should return session key.
},
dataType: 'json'
});
}
function last_fm_sign(params){
ss = "";
st = [];
so = {};
Object.keys(params).forEach(function(key){
st.push(key); // Get list of object keys
});
st.sort(); // Alphabetise it
st.forEach(function(std){
ss = ss + std + params[std]; // build string
so[std] = params[std]; // return object in exact same order JIC
});
// console.log(ss + last_fm_data['secret']);
// api_keyAPIKEY1323454formatjsonmethodauth.getSessiontokenTOKEN876234876SECRET348264386
hashed_sec = unescape(encodeURIComponent($.md5(ss + last_fm_data['secret'])));
so['signature'] = hashed_sec; // Correct when calculated elsewhere.
return so; // Returns signed POSTable object
}
Anything anyone can see that i'm missing here? I'm absolutely stumped why this isn't returning a correctly signed POSTable object in the format requested here. Thanks for your time.
Edit: can't thank anyone for their time if i don't get any advice! No one had any experience with last.fm?
So on testing some of the responses, I found the solution. There were 2 issues. EDITED see below ( The first was needing to remove
data['format'] = 'json';
as George Lee pointed out. Thanks George. )
The other issue was that I'd named a variable incorrectly so was being POSTed with the wrong name. The line
so['signature'] = hashed_sec;
should have been
so['api_sig'] = hashed_sec;
I noticed this in Pankaj's answer but unfortunately the rest of his answer (i.e. including the method) was incorrect. Making these 2 changes resolved the call and signed it correctly.
Thanks for all the suggestions!
EDIT: After some more playing, i've found that
data['format'] = 'json';
IS correct, however it DOESN'T get hashed with the signature.
Adding data['format'] = 'json';
to the POST object after hashing works, and in this instance will return JSON as opposed to XML - which was the preferred method. Adding after hashing is not documented anywhere that I can find, so there you go.
The new working code is as follows, and this shows the 2 lines indicated with --------------------
// Set elsewhere but hacked into this example:
var last_fm_data = {
'last_token':'TOKEN876234876',
'user': 'bob',
'secret': 'SECRET348264386'
};
// Kick it off.
last_fm_call('auth.getSession', {'token': last_fm_data['last_token']});
// Low level API call, purely builds a POSTable object and calls it.
function last_fm_call(method, data){
// param data - dictionary.
last_fm_data[method] = false;
// Somewhere to put the result after callback.
// Append some static variables
data['api_key'] = "APIKEY1323454";
data['method'] = method;
post_data = last_fm_sign(data);
// THEN ADD THE FORMAT ---------------------------------------
post_data['format'] = 'json';
$.ajax({
type: "post",
url: last_url,
data: post_data,
success: function(res){
last_fm_data[method] = res;
console.log(res['key'])// Should return session key.
},
dataType: 'json'
});
}
function last_fm_sign(params){
ss = "";
st = [];
so = {};
Object.keys(params).forEach(function(key){
st.push(key); // Get list of object keys
});
st.sort(); // Alphabetise it
st.forEach(function(std){
ss = ss + std + params[std]; // build string
so[std] = params[std]; // return object in exact same order JIC
});
// console.log(ss + last_fm_data['secret']);
// api_keyAPIKEY1323454formatjsonmethodauth.getSessiontokenTOKEN876234876SECRET348264386
hashed_sec = unescape(encodeURIComponent($.md5(ss + last_fm_data['secret'])));
so['api_sig'] = hashed_sec; // RENAMED THIS ----------------------------
return so; // Returns signed POSTable object
}
After investigating your code and other posts related to last.fm
api call, I found that @george lee in fact is correct. You don't need to provide format
while generating the auth_sign
.
Apart from that you need to apply $.md5()
to auth_sign
string after applying encodeURIComponent()
and unescape()
functions. Like this.
hashed_sec = $.md5(unescape(encodeURIComponent(ss + last_fm_data['secret'])));
Also while making ajax
call you need to pass api_key, token & api_sig
as data
. But seeing your code, reveals that you are passing api_key, token, format, method & signature
.
So you need to remove format, method & signature
from the data
field of ajax
call.
Instead you need to pass api_key, token & api_sig
to the data
field.
So the final code after commenting the data['format'] = 'json';
line will look like this.
// Set elsewhere but hacked into this example:
var last_fm_data = {
'last_token':'TOKEN876234876',
'user': 'bob',
'secret': 'SECRET348264386'
};
// Kick it off.
last_fm_call('auth.getSession', {'token': last_fm_data['last_token']});
// Low level API call, purely builds a POSTable object and calls it.
function last_fm_call(method, data){
// param data - dictionary.
last_fm_data[method] = false;
// Somewhere to put the result after callback.
// Append some static variables
data['api_key'] = "APIKEY1323454";
//data['format'] = 'json';
data['method'] = method;
post_data = last_fm_sign(data);
$.ajax({
type: "POST",
url: last_url,
data: post_data,
success: function(res){
last_fm_data[method] = res;
console.log(res['key'])// Should return session key.
},
dataType: 'json'
});
}
function last_fm_sign(params){
ss = "";
st = [];
so = {};
so['api_key'] = params['api_key'];
so['token'] = params['token'];
Object.keys(params).forEach(function(key){
st.push(key); // Get list of object keys
});
st.sort(); // Alphabetise it
st.forEach(function(std){
ss = ss + std + params[std]; // build string
});
ss += last_fm_data['secret'];
// console.log(ss + last_fm_data['secret']);
// api_keyAPIKEY1323454formatjsonmethodauth.getSessiontokenTOKEN876234876SECRET348264386
hashed_sec = $.md5(unescape(encodeURIComponent(ss)));
so['api_sig'] = hashed_sec; // Correct when calculated elsewhere.
return so; // Returns signed POSTable object
}
Please refer to this link.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With