1

Hi I'm trying to convert the PHP Code example for an AWIS request found here:

http://aws.amazon.com/code/AWIS/402

to javascript / jquery. I feel like I'm very close, but I'm getting an "unauthorized" 401 response. I would love to get this up and running, and I was wondering if anyone could tell me what I'm doing wrong, or point me in the direction of an example using javascript.

<html>
    <head>
        <title>AWIS</title>
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
        <script type="text/javascript" src="http://crypto-js.googlecode.com/files/2.3.0-crypto-sha256-hmac.js"></script>
        <script type="text/javascript" src="http://crypto-js.googlecode.com/files/2.3.0-crypto-min.js"></script>
    </head>
    <body>
        <div id="output"></div>
        <script>
        var actionName = 'UrlInfo';
        var responseGroupName = 'Rank,LinksInCount';
        var serviceHost = 'awis.amazonaws.com';
        var count = 10;
        var startNum = 1;
        var sigVersion = '2';
        var hashVersion = 'HmacSHA256';
        var accessKeyID = 'XXXAJA664T37BDNPSXXX';
        var accessKey = 'XXXoImq0sZ4J/vYRewLuNjPFXYQ809DfLmzcpXXX';
        var site = 'http://site.com';

        function getURLInfo(){
            var queryParams = buildQueryParams();
            var sig = generateSignature(queryParams);
            var requestURL = 'http://' + serviceHost + '/?' + queryParams + '&Signature=' + sig;
            console.log('requestURL:' + requestURL);

            $.ajax({
                type: 'GET',
                url: requestURL,
                dataType: 'xml',
                crossDomain: true,
                error: function(jqXHR,textStatus, errorThrown){
                    console.log(textStatus + "|" + errorThrown)
                },
                success: function(data, textStatus, jqXHR){
                    console.log(data);
                }
            });


        }

        function ISODateString(d){
          function pad(n){return n<10 ? '0'+n : n}
          return d.getUTCFullYear()+'-'
              + pad(d.getUTCMonth()+1)+'-'
              + pad(d.getUTCDate())+'T'
              + pad(d.getUTCHours())+':'
              + pad(d.getUTCMinutes())+':'
              + pad(d.getUTCSeconds())+'.000Z'
        }   

        function getTimeStamp(){
            var d = new Date();
            var now = ISODateString(d);
            //var hardcoded_time = "2011-10-28T16:33:03.000Z"; //USE THIS TO TEST BETWEEN SAMPLE PHP AND JS
            return now;
        }

        function buildQueryParams(){
            var params = {};
            params.AWSAccessKeyId = accessKeyID;
            params.Action = actionName;
            params.Count = count;
            params.ResponseGroup = responseGroupName;
            params.SignatureMethod = hashVersion;
            params.SignatureVersion = sigVersion;
            params.Start = startNum;
            params.Timestamp = getTimeStamp();
            params.Url = site;

            paramString = $.param(params);
            return paramString;
        }

        function generateSignature(sigParams){
            var sign = "GET\n" + serviceHost + "\n/\n" + sigParams;
            console.log("SIGN: \n" + sign);
            var sigHash = Crypto.HMAC(Crypto.SHA256, sign, accessKey, { asString: true });
            console.log("HMAC:" + sigHash);
            var sigBytes = Crypto.charenc.Binary.stringToBytes(sigHash);
            var sig64 = Crypto.util.bytesToBase64(sigBytes);
            console.log("BASE 64: " + sig64);
            var sigEnc = encodeURIComponent(sig64);
            console.log("ENCODED URL: " + sigEnc)
            return sigEnc;
        }

        function awisResponse(response){
            console.log(response);
        }

        getURLInfo();

        </script>
    </body>
</html>
4
  • As to your last question, wouldn't it generally be a bad idea to have a secret key sitting on the client? Commented Oct 26, 2011 at 23:36
  • Also... am I missing the part where you're doing the base64 encoding? Commented Oct 26, 2011 at 23:41
  • Yes and yes. I've modified the encoding for base64 - but I still can't seem to get in. As for the access key - I don't really understand how you would accomplish this any other way. Even if I use PHP, the key has to reside in the code somewhere, right? What's the best way to retrieve the access key value? Commented Oct 27, 2011 at 2:26
  • 1
    There's a big difference between code on the server and code on the client. If it's in PHP code, someone would have to steal your code. If it's in client side javascript, someone gets that code everytime they use your application. For a personal site, this is probably fine. Commented Oct 27, 2011 at 12:03

2 Answers 2

1

[UPDATE] Derp. I think I pointed out several outstanding issues here, but must admit I still wasn't quite catching onto the problem of cross site.

The final piece of the puzzle is using a proxy so PHP can get the xml. So this isn't a javascript only solution, but mostly it is: http://benalman.com/projects/php-simple-proxy/ [/UPDATE]

I'd prefer you don't give up on this for the wrong reason. You'll remember that the X in AJAX stands for XML :)

So two ideas based on duskwuff's commments.

  • You surely can parse XML with javascript. The function you are using already supports 'xml' as a datatype.
  • It seems like you are going wrong with the callback part. You can pass a callback function to either complete, success, and/or error.

Here's the code:

$.ajax({
    type: 'GET',
    url: requestURL,
    dataType: 'xml',
    error: function(jqXHR, textStatus, errorThrown){
        console.log(textStatus);
    },
    success: function(data, textStatus, jqXHR){
        console.log(data);
    }
});

Your more pressing problem is in the authentication, but perhaps you will be able to get more details now using the error function and it's errorThrown argument.

My recommendation is to see if you can get the URL generated in another context and compare that to what you have to see if you're doing it right (it's been a few months, but I thought I remember there being an amazon tool that generated the request URL for you).

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks! - I've edited my code above - I actually have working PHP code to generate the request, but then I have to change my whole code around to use PHP instead of Javascript. I've included a comparison of the two requests. When I use your code to log errors, it doesn't give me much info in the error response (error | unauthorized). However, if I use the signature request URL right in the browser, it returns a response saying access credentials are missing, while the php version returns a successful response, so there must be a problem with generating the signing request.
Well, clearly there is a problem with your signature since they don't match. To debug, you can look at each step and compare further: -before the SHA256, after, after the HMAC, after the base 64 encoding.
Ah - I think it's the HMAC - see modified description above - I'm not sure how to get it into the format that PHP attains
I actually compared the difference between HMAC for php and javascript and got equivalent results, so I don't think that is the issue (when comparing make sure you hardcode timestamp). I did notice 2 mistakes in your code which will make a big difference: 1. You mispelled the s"AWSAccessKeyID", it should be lowercase 'd' as in AWSAccessKeyId. That's why you see "access credentials are missing." Also, the reference to "$.serviceHost" is undefined. That should be simply serviceHost. I hope that helps.
"I'm afraid duskwuff might be right though." I've already shown you that duswkwuff is wrong: the callbacks are handled on your side by jquery, xml is supported out of the box, there's absolutely no reason this wouldn't work in javascript. Post the inputs where they're same and the outputs where they're different and we'll get this thing solved.
|
1

The Alexa Web Information Service does not support JSONP operation (e.g, the callback parameter you're providing is ignored; the response is always XML). As such, there is no way to use it from JavaScript.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.