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.
The flow is as follows:
- 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.
- 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
- The user information (access_token, refresh_token, and mobile number) is saved to a DynamoDB table
- An SNS topic receives the mobile number as a message
- A success message is sent back to the end user.
- The SNS topic (step 4) calls a second Lambda function.
- The Create_User_Topic lambda function creates a sns topic for the newly authenticated user.
- Once the topic is created, a notice is sent to the user’s mobile device – asking to confirm subscription.
- 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
- Working Demo
- Part 1 – Overview
- Part 2 – Authentication Flow
- Part 3 – Notification Flow (coming Mon, Feb 22)