Search code examples
amazon-web-servicesoauthpostmanalexa-skills-kit

Client authentication failed in Postman request for Amazon Alexa Smart Home Skill LWA


I am referring to Amazon documentation for the purpose of Customer Authentication. Currently, I am using LWA.

Steps I followed:

  1. I enabled the Send Alexa Events Permission from the Alexa developer Console in Build > Permission page.

  2. I took the grant code from the request in the cloudwatch logs which was sent when I logged in using Alexa companion app.

Example:-

    {
     "directive": {
         "header": {
             "messageId": "Example",
             "name": "AcceptGrant",
             "namespace": "Alexa.Authorization",
             "payloadVersion": "3"
         },
         "payload": {
            "grant": {
                "code": "Example2",
                "type": "OAuth2.AuthorizationCode"
            },
            "grantee": {
                "token": "Example3",
                "type": "BearerToken"
            }
         }
      }
    }
  1. Permission Page under build on Alexa Developer console gave me client-Id and client-secret Which I used for making the post request to https://api.amazon.com/auth/o2/token.

Example:-

 POST /auth/o2/token HTTP/l.l
 Host: api.amazon.com
 Content-Type: application/x-www-form-urlencoded;charset=UTF-8 
 grant_type=authorization_code&code=&client_id=&client_secret=

I passed the code,client_id, and client_secret in the above example and made the post request to this URL https://api.amazon.com/auth/o2/token

  1. I tried using x-www-form-urlencoded;charset=UTF-8 and also JSON for the Content-Type.

I followed the step given in the above documentation and I am stuck on the error ( 401 Unauthorized ):

{
    "error_description": "The request has an invalid grant parameter : code",
    "error": "invalid_grant"
}

I tried implementing it using Python code and Postman both. Ending up with the Same above error scenario.


Solution

  • Here is a sample code to help you and others who are looking to send events to alexa gateway.

        const AWS = require('aws-sdk');
        AWS.config.update({region: 'eu-west-1'});
        
        // Create the DynamoDB service object
        const ddb = new AWS.DynamoDB({ apiVersion: 'latest' });
        const doc = new AWS.DynamoDB.DocumentClient({
                    convertEmptyValues: true,
                    service: ddb
                });        
        
        // Using 'request' for http POST and GET request.
        // https://www.npmjs.com/package/requests
        // npm install --save requests 
        const r = require('request');
    
        //Handle Authorization. Call this method from your lambda handler whenever you get Alexa.Authorization message. You will get this message only when you select permission to 
        //send events in your Smart Home Skill.
        //Access to Event gateway allows you to enable Proactive Device Discovery and 
        //Proactive State Reporting in your skill
        //More information on Alexa.Authorization can be found on https://developer.amazon.com/docs/device-apis/alexa-authorization.html
        function handleAuthorization(request, context, user) {  
        
            //Even when you are using your own authentication, the url below will still
            //point to amazon OAuth token url. The token you obtain here has to be stored
            //separately for this user. Whenever sending an event to alexa event gateway you will
            //require this token.
            //URL below is for EU server. Look at following documentation link to identify correct url
            //for your system.
            //https://developer.amazon.com/docs/smarthome/send-events-to-the-alexa-event-gateway.html
            var url = "https://api.amazon.com/auth/o2/token";
            var body = {
                grant_type : 'authorization_code',
                code : request.directive.payload.grant.code,
                client_id : 'your client id from permissions page on developer portal where you enable alexa events. This is id different than one you specify in account linking settings',
                client_secret : 'client secret from permissions page'
            }
            
            //https://developer.amazon.com/docs/smarthome/authenticate-a-customer-permissions.html
            r.post({
              url:     url,
              form :  body
            }, function(error, response, b){    
                if (error) { return console.log(error); }
                var body = JSON.parse(b);
                var params = {
                  TableName: 'Devices',
                  Item: {
                    'id' : user,
                    'auth_token' : body.access_token,
                    'refresh_token' : body.refresh_token
                  }
                }
                log("DEBUG:", "Authorization Body", JSON.stringify(body));
                log("DEBUG:", "Authorization Response", JSON.stringify(response));
                log("DEBUG:", "Database Params", JSON.stringify(params));
                
                // Call DynamoDB to add the item to the table
                var putObjectPromise = doc.put(params).promise();
                //Store auth_token and refresh_token in database. We will need these
                //while sending events to event gateway.
                //Send a success response.
                putObjectPromise.then(function(data) {
                    var response = {
                      event: {
                        header: {
                          messageId: request.directive.header.messageId,
                          namespace: "Alexa.Authorization",
                          name: "AcceptGrant.Response",
                          payloadVersion: "3"
                        },
                        "payload": {
                        }
                      }
                    };
            
                    context.succeed(response);
                }).catch(function(err) {
                    //TODO - Add a Authorization error response JSON here.      
                    console.log(err);
                });                         
            });                   
        }