Using AWS Cognito with Node.JS - Part 2


Facebook Sign in with Passport

back to Part 1
The complete code for the tutorial is at GitHub.

Set up node.js on a US-East region Amazon Linux EC2 instance and configure firewall settings for HTTP access.
On your EC2 instance create a new app using the express generator.
$ express CognitoExample
$ cd CognitoExample
$ npm install

Do npm start and check your browser that you see the default Express page OK.
Install Passport, Passport-Facebook, the AWS javascript SDK and colors (for multi colored console output). Use --save to add to the package.json file.
$ npm install passport --save
$ npm install passport-facebook --save

$ npm install aws-sdk --save
$ npm install colors --save

Open in your editor www from the bin folder and change port to 8080.
Open in your editor index.jade from the views folder and change the Express sample page to a Facebook login page:
p Please log in. We  only have access to your name and facebook id number. We do not collect  sensitive information such as email addresses.
a(href='/auth/facebook') Sign in with Facebook

Open in your editor index.js from the routes folder and add:
var passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;
var AWS = require('aws-sdk');
var colors = require('colors');

Next add variables:
AWS_ACCOUNT_ID - This is your AWS account number.
COGNITO_IDENTITY_POOL_ID - You can get this from your Cognito dashboard by selecting Edit Identity Pool
IAM_ROLE_ARN - This is the IAM role created when you created your Cognito pool. You can get this from the the main Services menu - IAM - Roles - then select the role for your identity pool.
FACEBOOK_APP_ID and FACEBOOK_APP_SECRET - From the facebook app page.

var AWS_ACCOUNT_ID = 'XXXXXXXX';
var AWS_Region = 'us-east-1';
var COGNITO_IDENTITY_POOL_ID = 'us-east-XXXXXXXXXXXXXXXXXXX';
var COGNITO_IDENTITY_ID, COGNITO_SYNC_TOKEN, AWS_TEMP_CREDENTIALS;
var cognitosync;
var IAM_ROLE_ARN = 'arn:aws:iam::XXXXXXXXX:role/Cognito_AWSCognitoTutorialAuth_DefaultRole';
var COGNITO_SYNC_COUNT;
var COGNITO_DATASET_NAME = 'TEST_DATASET';
var FACEBOOK_APP_ID = 'XXXXXXXXXXXX';
var FACEBOOK_APP_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXX';
var FACEBOOK_TOKEN;
var FACEBOOK_USER = {
  id: '',
  first_name: '',
  gender: '',
  last_name: '',
  link: '',
  locale: '',
  name: '',
  timezone: 0,
  updated_time: '',
  verified: false
};
var userLoggedIn = false;
var cognitoidentity = new AWS.CognitoIdentity();
Now let's add our code for Passport to collect the Facebook token:
router.use(passport.initialize());
router.use(passport.session());

passport.use(new FacebookStrategy({
  clientID: FACEBOOK_APP_ID,
  clientSecret: FACEBOOK_APP_SECRET,
  callbackURL: 'http://dev.ap-robotics.com/auth/facebook/callback'
}, function(accessToken, refreshToken, profile, done) {
  process.nextTick(function() {
    FACEBOOK_TOKEN = accessToken; 
    FACEBOOK_USER = profile._json;
    userLoggedIn = true;
    done(null, profile);
  });
}));

passport.serializeUser(function(user, done) {
  done(null, user);
});

passport.deserializeUser(function(obj, done) {
  done(null, obj);
});
Now lets add our success and error callbacks:
/* GET Facebook page. */
router.get('/auth/facebook', passport.authenticate('facebook'));

/* GET Facebook callback page. */
router.get('/auth/facebook/callback', passport.authenticate('facebook', {
  successRedirect: '/success',
  failureRedirect: '/error'
}));

/* GET Facebook success page. */
router.get('/success', function(req, res, next) {
  console.log('FACEBOOK_TOKEN:'.green + FACEBOOK_TOKEN); 
  res.send('Logged in as ' + FACEBOOK_USER.name + ' (id:' + FACEBOOK_USER.id + ').');
});

/* GET Facebook error page. */
router.get('/error', function(req, res, next) {
  res.send("Unable to access Facebook servers. Please check internet connection or try again later.");
});

Now run the app with npm start and you should get the success page after you have logged in from the browser.:
Logged in as Paul Coady (id:XXXXXXXXXXXXXX).
The console output should be something like:
GET / 200 395ms - 338b
GET / 200 18ms - 338b
GET /stylesheets/style.css 200 5ms - 110b
GET /auth/facebook 302 4ms - 388b
GET /auth/facebook/callback?code=AQD4e7zDMnHkQxtEO-XXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXX 302 347ms - 72b
FACEBOOK_TOKEN:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
GET /success 304 2ms
Now that you have your Facebook token you can now use it to get the CognitoID credentials.

Get CognitoID Credentials


Now it's time to pass our Facebook token over to Cognito. Prior to version v2.0.14 of the AWS Javascript SDK this was a difficult process involving calls to IAM and STS. A new object CognitoIdentityCredentials has greatly simplified the CognitoID credentials process by removing the need to create STS tokens and temporary IAM credentials yourself.
We are going to create a new function to get our CognitoID credentials. Open index.js in your editor and add a call to the new function getCognitoID() in the callback of our success page.
 /* GET Facebook success page. */
router.get('/success', function(req, res, next) {
  console.log('FACEBOOK_TOKEN:'.green + FACEBOOK_TOKEN); 
  getCognitoID();
  res.send('Logged in as ' + FACEBOOK_USER.name + ' (id:' + FACEBOOK_USER.id + ').');
});
Now lets create the function:
 function getCognitoID(){
  var params = {
    AccountId: AWS_ACCOUNT_ID, /* required */
    RoleArn: IAM_ROLE_ARN,  /* required */
    IdentityPoolId: COGNITO_IDENTITY_POOL_ID, /* required */
    Logins: {
      'graph.facebook.com': FACEBOOK_TOKEN
    } 
  };
  AWS.config.region = AWS_Region;
  /* initialize the Credentials object */
  AWS.config.credentials = new AWS.CognitoIdentityCredentials(params);
  /* Get the credentials for our user */
  AWS.config.credentials.get(function(err) {
    if (err) console.log("credentials.get: ".red + err, err.stack); /* an error occurred */
      else{
&nbsp &nbsp &nbsp &nbsp AWS_TEMP_CREDENTIALS = AWS.config.credentials.data.Credentials;
        COGNITO_IDENTITY_ID = AWS.config.credentials.identityId;
        console.log("Cognito Identity Id: ".green + COGNITO_IDENTITY_ID);
      }
  });
}
Now run the app with npm start again and you should get something like the following from the console after you have logged in from the browser.
GET / 304 322ms
GET /stylesheets/style.css 304 3ms
GET /auth/facebook 302 4ms - 388b
GET /auth/facebook/callback?code=XXXXXXXXXXXXXX_-XXXXXXXXX-XXXXXXXXXXXXX_XXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXX_XXX--XXX-XXXXXXXXXXX-XXXXX-XXXXX_XXXXXXXXXXXXXXX-XXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXX 302 327ms - 72b
FACEBOOK_TOKEN:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
GET /success 304 36ms
Cognito Identity Id: us-east-1:XXXXXXX-XXXX-XXX-XXX-XXXXXXXX
Now that we have our CognetoID credentials we can get our CognetoSync session token.

Get CognitoSync Session Token


Now that we have our CognitoID credentials we can use these to access CognitoSync. First we need to use our new temporary credentials to create a CognitoSync session token.
We are going to create a new function to get our CognitoSync session token. Open index.js in your editor and add a call to the new function getCognitoSynToken() in the callback of getCognitoID().
 function getCognitoID(){
  var params = {
    AccountId: AWS_ACCOUNT_ID, /* required */
    RoleArn: IAM_ROLE_ARN,  /* required */
    IdentityPoolId: COGNITO_IDENTITY_POOL_ID, /* required */
    Logins: {
      'graph.facebook.com': FACEBOOK_TOKEN
    } 
  };
  AWS.config.region = AWS_Region;
  /* initialize the Credentials object */
  AWS.config.credentials = new AWS.CognitoIdentityCredentials(params);
  /* Get the credentials for our user */
  AWS.config.credentials.get(function(err) {
    if (err) console.log("credentials.get: ".red + err, err.stack); /* an error occurred */
      else{
&nbsp &nbsp &nbsp &nbsp AWS_TEMP_CREDENTIALS = AWS.config.credentials.data.Credentials;
        COGNITO_IDENTITY_ID = AWS.config.credentials.identityId;
        console.log("Cognito Identity Id: ".green + COGNITO_IDENTITY_ID);
        getCognitoSynToken();
      }
  });
}
In order to get the token we must make a call to list records. If our dataset doesn't exist (as is the case now) it will be created automatically. We also get the sync count for the dataset which is needed later to add or change dataset records.
Now lets create the function:
 function getCognitoSynToken(){
  /* Other AWS SDKs will automatically use the Cognito Credentials provider */
  /* configured in the JavaScript SDK. */
  cognitosync = new AWS.CognitoSync();
  cognitosync.listRecords({
    DatasetName: COGNITO_DATASET_NAME, /* required */
    IdentityId: COGNITO_IDENTITY_ID,  /* required */
    IdentityPoolId: COGNITO_IDENTITY_POOL_ID  /* required */
  }, function(err, data) {
    if (err) console.log("listRecords: ".red + err, err.stack); /* an error occurred */
      else {
        console.log("listRecords: ".green + JSON.stringify(data));
        COGNITO_SYNC_TOKEN = data.SyncSessionToken;
        COGNITO_SYNC_COUNT = data.DatasetSyncCount;
        console.log("SyncSessionToken: ".green + COGNITO_SYNC_TOKEN);           /* successful response */
        console.log("DatasetSyncCount: ".green + COGNITO_SYNC_COUNT);
      }
  });
}
Now run the app with npm start again and you should get something like the following from the console after you have logged in from the browser.
GET / 304 318ms
GET / 200 17ms - 338b
GET /stylesheets/style.css 304 2ms
GET /auth/facebook 302 4ms - 388b
GET /auth/facebook/callback?code=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 302 348ms - 72b
FACEBOOK_TOKEN:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
GET /success 304 17ms
Cognito Identity Id: us-east-1:XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
listRecords: {"Count":1,"DatasetDeletedAfterRequestedSyncCount":false,"DatasetExists":true,"DatasetSyncCount":1,"LastModifiedBy":"XXXXXXXXXXXX","Records":[{"DeviceLastModifiedDate":"2014-08-15T15:33:58.627Z","Key":"USER_ID","LastModifiedBy":"XXXXXXXXXX","LastModifiedDate":"2014-08-15T15:33:58.627Z","SyncCount":1,"Value":"XXXXXXXXXXXX"}],"SyncSessionToken":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}
SyncSessionToken: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
DatasetSyncCount: 1

Ok...Now that we have our CognetoSync session token we can use this to do something useful like add records to our dataset or download a file.
In Part3 we will finish off with some examples of using Cognito to access AWS resources.
Paul Coady CEO BackSpace Technology LLC

Providing the best value AWS certification courses and exam engines.

2 comments:

  1. Great Example!!! Would you happen to have a e.g. github link where I can download index.js and index.jade. I'm trying to make sure that I didn't make any mistakes in my edits.

    Thanks
    Bernie

    ReplyDelete
  2. Hi Bernie,
    Glad you enjoyed it. I wrote it a long time ago straight after Cognito was released. Cognito documentation was pretty poor at the time. Hopefully everything is still current.
    There is a link at the top of the page for the code.
    All the best,
    Paul

    ReplyDelete