A Fitbit Low Battery SMS Notification using AWS – Part 2

In Part 1, I introduced an app that sends an SMS notification when your Fitbit battery is low. Part 2 covers the Authentication Flow of the process.

Fitbit-SMS-Auth

The flow is as follows:

  1. User accesses the sign-up page (and by default, provides authorization via Fitbit.com). The request is passed through an API Gateway to a Lambda function. The Lambda function calls a Fitbit api to authenticate the user.
  2. The Fitbit authentication returns to the Lambda code. From there: (3) the token is saved to a DynamoDB database, (4) a message is placed on a SNS topic, and (5) a success response is sent back to the webpage
  3. The user information (access_token, refresh_token, and mobile number) is saved to a DynamoDB table
  4. An SNe1079abb478cc4e04d13615bc10aa4ca[1]S topic receives the mobile number as a message
  5. A success message is sent back to the end user.
  6. The SNS topic (step 4) calls a second Lambda function.
  7. The Create_User_Topic lambda function creates a sns topic for the newly authenticated user.
  8. Once the topic is created, a notice is sent to the user’s mobile device – asking to confirm subscription.
  9. The user confirms the subscription.

A few call-outs:

  • I originally planned to have the sign-up request come from PHP to a SQS queue, but Lambda cannot be automatically triggered from a queue. So I used an API gateway.
  • I restricted the API gateway to only accept calls from the EC2 IP address, as a second layer of security.
  • There was no real need to use DynamoDB – I used it primarily because it provides cross-region availability without replication.
  • I decoupled my Lambda functions in case I later decide to use a professional service to send the SMS messages.
  • Lambda runs functions asynchronously.. which means I had to nest functions if a response was needed before the next function could be executed. There’s probably a cleaner way to do this with async await… but the nesting method was good enough for this exercise.

I won’t cut & paste all the code here (there are some great examples online, and I’m happy to share more details asked in the comments). I will share the Authentication function… in case there is someone out there wondering how to call the Fitbit APIs from node.js.


var aws = require('aws-sdk');  
var ddb = new aws.DynamoDB();
var sns = new aws.SNS();

exports.handler = function(event, context) {

    var client_id = 'Client ID From Fitbit';
    var client_secret = 'Client Secret From Fitbit';
    var table = "Fitbit_Authorization_Table";
    var code = event.code;
    var mobile = '1-' + event.mobile
    mobile = mobile.replace(/[^\w\s]/gi, ''); //remove crazy characters
    

    var AuthorizationHeader = new Buffer(client_id + ':' + client_secret).toString('base64');
    
    var https = require('https');
    var options = {
        host :  'api.fitbit.com',
        port : 443,
        path : '/oauth2/token?grant_type=authorization_code&code='+code,
        method : 'Post',
        headers: {'Authorization': 'Basic '+ AuthorizationHeader, 'Content-Type': 'application/x-www-form-urlencoded'}
    };
    
//Get Fitbit Authorization
    var getReq = https.request(options, function(res) {
        res.on('data', function(data) {
            
            var json_result = JSON.parse(data);
            var createtopic_data;
           
             var item = {
                "Mobile": {'S': mobile},
                "Authorization_Code": {'S': code},
                "Access_Token": {'S': json_result.access_token},
                "Refresh_Token": {'S': json_result.refresh_token},
                "Token_Type": {'S': json_result.token_type},
                "Device_Details": {'S': 'empty'},
                "User_ID": {'S': json_result.user_id},
                "Active": {'S': '0'}
            };
        
            
            
            ddb.putItem({'TableName': table,'Item': item }, function(err, data) {
                if (err){ console.log(err); console.log(item);}
                else {
                    
                    sns.publish({
                        Message: mobile,
                        Subject: "Mobile to create SNS",
                        TopicArn: 'SNS_TOPIC_TO_SEND_TO_NEXT_FUNCTION'}, function(err, data) {
                        if (err) {console.log(err.stack);
                        return;
                        }
                        else{
                            console.log("sent to sns") ;
                            context.done(null, {"Code":code, "Mobile":mobile});
                            
                        }
                    });

                }
         
            });
            

             
        });
    });
    
    //end the request
    getReq.end();
    getReq.on('error', function(err){
        console.log("Error: ", err);
    });

}

Related Posts

Leave a Reply