Authentication (Messenger chatbot)

This article explains how to integrate an authentication on a messenger chatbot (account linking). You should be familiar with messenger platform.

This article focuses on authentication page integration, authorization response handling, access tokens, ID tokens and user profile data retrieval.

Step 1: Integrate an authentication page for handling messenger account linking process.

This page will be fired when clicking on a login button event from messenger.

The user authenticates via email/password or social login. This process must be implemented using the suitable ReachFive SDK.

ReachFive’s Identity SDKs allow different authentication flows. For the messenger account linking, you must use a messenger_code response type.

For security reasons, you must set the https://facebook.com/messenger_platform/account_linking/ in the Allowed Callback URLs field, within your ReachFive Console.

Example - ReachFive Identity SDK for Web
reach5('showAuth', {
  container: 'auth-container',
  auth: {
    responseType: 'messenger_code',
    // It will be provided by messenger in the URL query params when clicking on the message login button
    // To set it, parse redirect_uri in the query params.
    redirectUri: 'https://facebook.com/messenger_platform/account_linking/?account_linking_token=ARRBYkDqtMVAjnXxmW1q9Yb7WA5rnOsePJ-Zz2LypkM70ju9k-e0N3FaqnEt2mZMON0Azcvdd2uvdv7N3Crx4kePyh3WuHZoKMngX9AsKvbMDw'
  }
});

Messenger requires all login redirects to use HTTPS. For example, this page should be accessible on https://www.example.com/messenger-login.

Step 2: Handle the Authorization Response

After the authentication process, ReachFive responds to your application by redirecting the user to the messenger callback URL or the authentication page with an error in the query string.

If the authentication succeeded, then the authentication page closes and an messaging_account_linking event response containing an authorization code is sent. If not (e.g. when the user does not approve access to social data), the response contains an error message. The error message that is returned to your web server appears in the query string, as shown below.

An error response:

https://www.example.com/messenger-login?error=access_denied

Step 3: Exchange authorization code for ID token

The messenger platform sends the authorization code on a message_account_linking event. You should recover this event with a configured webhook endpoint. The authorization code can be exchanged for an ID token using an HTTP request to the ReachFive token endpoint.

The string YOUR_REACHFIVE_DOMAIN should be replaced with the domain of your ReachFive account and the string YOUR_REACHFIVE_CLIENT_ID should be replaced with your client ID of your ReachFive account.

For more information, read How-To get your ReachFive Domain and Credentials.

POST /oauth/token HTTP/1.1
Host: https://YOUR_REACHFIVE_DOMAIN
Content-Type: application/x-www-form-urlencoded

code=A8sLD49d-lPcKyUwBaSm4oThfjp4
&client_id=YOUR_REACHFIVE_CLIENT_ID
&redirect_uri=https://facebook.com/messenger_platform/account_linking/
&grant_type=authorization_code
{
  "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1N...",
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIU...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Step 4: Retrieve user’s profile data from ID Token

After your application obtains an id_token, you can use the token to retrieve the user’s profile data by parsing it. An id_token is a JWT (JSON Web Token), that is, a cryptographically signed Base64-encoded JSON object. As you are communicating directly with ReachFive over HTTPS channel, it is normally not critical that you validate your id_token before parsing it. However most API libraries combine the validation with the work of decoding the base64 and parsing the JSON.

var decodedToken = jwt.verify(idToken, clientSecret);
{
  "sub": "AVPw-jHcQG5c_BvJk9e_",
  "given_name": "John",
  "family_name": "Doe",
  "email": "john.doe@example.com"
}

Complete example

The following example is developed with NodeJS/Express stack, but it supposed to be simple enough to be easily transposed in another language.

var express = require('express');
var request = require('request');
var jwt = require('jsonwebtoken');
var router = express.Router();

router.get(
  '/messenger-login',
  function(req, res) {
    res.render('index');
  }
);

router.post(
  '/messenger-webhook',
  function(req, res) {
    /*
      You must send back a status of 200(success) within 20 seconds
      to let messenger know you've successfully received the callback.
      Otherwise, the request will time out.
      When a request times out from Facebook messenger the service attempts
      to resend the message.
      This is why it is good to send a response immediately so you
      don't get duplicate messages in the event that a request takes
      a while to process.
    */
    res.sendStatus(200);

    const data = req.body;

    // Make sure this is a page subscription
    if (data.object === 'page') {
      // Iterate over each entry
      // There may be multiple if batched
      data.entry.forEach((pageEntry) => {
        if (!pageEntry.messaging) {
          return;
        }
        // Iterate over each messaging event
        pageEntry.messaging.forEach((messagingEvent) => {
          console.log({messagingEvent});
          if (messagingEvent.account_linking && messagingEvent.account_linking.status === 'linked') {
            // This information is available in your ReachFive account's settings
            var domain = 'YOUR_REACHFIVE_DOMAIN';
            var clientId = 'YOUR_REACHFIVE_CLIENT_ID';
            var clientSecret = 'YOUR_REACHFIVE_CLIENT_SECRET';

            // This is the authorization code to exchange
            var code = messagingEvent.account_linking.authorization_code

            // This is the redirect uri corresponding to the current callback
            var redirectUri = 'https://facebook.com/messenger_platform/account_linking/';

            // Send an POST HTTP request using the "application/x-www-form-urlencoded"
            // format to ReachFive's token endpoint
            request.post(
              'https://' + domain + '/oauth/token',
              {
                form: {
                  'code': code,
                  'client_id': clientId,
                  'redirect_uri': redirectUri,
                  'grant_type': 'authorization_code'
                }
              },
              function(err, response, body) {
                if (!err) {
                  // Unexpected error
                  return;
                }
                if (response.statusCode != 200) {
                  // Expected error
                  // Those types of error can occurred when the authorization code is expired for example.
                  return;
                }

                // Success response

                // Parse JSON response
                var authResult = JSON.parse(body);

                // Retrieve id token from response
                var idToken = authResult['id_token'];

                // Parse and validate ID Token
                var decodedToken = jwt.verify(idToken, clientSecret);

                var id = decodedToken.sub;
                var givenName = decodedToken.given_name;
                var familyName = decodedToken.family_name;
                var email = decodedToken.email;

                // Send a welcome message via messenger send API
                // ...
              }
            );
          } else {
            // handle other message events
          }
        });
      });
    }
  }
);