TCP/IP asynchronous server for sending APN and GCM in node.js

Task

Create async TCP/IP server for sending Apple Push Notifications and Google Cloud Messages to custom made mobile Apps.

Requirements

  • scalable asynchronous TCP/IP server
  • HTTP REST Api interface
  • token authentication on REST Api interface
  • run node.js as Windows service
  • authenticate users against windows domain or windows local account
  • send notifications to iOS and Android devices via GCM an APN service

I know that this is all possible in .NET world. But I wanted to do it differently with using node.js on Windows platform. One thing I was most worried about was whether I could authenticate user with username and password against domain. For all other requirements I was quite sure that are possible with node.js.

Scalable Asynchronous TCP/IP Server

This is the easiest part because node.js is designed for building light and scalable non-blocking applications.

Create HTTP REST Api interface

It is easy to host both TCP/IP server and HTTP server inside node.js application.

Token Authentication on REST API interface

I decided to use token authentication on API interface. For token authentication I used node.js jwt-simple package. Instead of sending username and password with every request to Api interface I rather exchanged credentials for token and then token is sent with every API call.

Run as Windows service

I decided to use windows-service npm package which enables to run node.js application as native Windows service.

Authenticate users against windows domain

This was the hardest challenge because it seemed there is no easy way to use Windows integrated authentication mechanism with node.js. One solution would be to use npm package passport-windowsauth. This solution uses LDAP server to authenticate user.

You will need 4 settings for LDAP:

  • url: this is usuall ldap://your-domain-controller-hostname-or-ip
  • base: this is the distinguish name of your domain, if you domain is foo, the DN will probabily be DC=foo,DC=com
  • bindDN: this is a user account, in Active Directory sysadmin hargon you will ask for a service account with password.
  • bindCredentials: this is the password of the service account.

I also tested another solution where I created .NET dll library where I authenticate user against server or domain with PrincipalContext class.

namespace WindowsAuth
{
public class Startup
{
    public async Task<object> Invoke(object input)
    {
        IDictionary<string, object> payload =                               
        (IDictionary<string, object>)input;
        bool Domain = (bool)payload["Domain"];
        string DomainName = (string)payload["DomainName"];
        string UserName = (string)payload["UserName"];
        string Password = (string)payload["Password"];

        return await Task.FromResult<bool>        (CheckAuth(UserName, Password, DomainName, Domain));

    }

    private static bool CheckAuth(string username, 
    string password, string domainName, bool domain)
    {
        PrincipalContext context;

        if (domain)
            context = 
            new PrincipalContext(ContextType.Domain,                 
            domainName);
        else
            context = new             
            PrincipalContext(ContextType.Machine);

        return context.ValidateCredentials(username,
        password);
    }
}
}

To integrate this function into node.js

var authFunc = require('edge').func('../WindowsAuth.dll');

exports.authenticate = function (input) {
authFunc(input, function (error, result) {
    if (result == true) {
        console.log('Authenticated');
    }
    else
        console.log('Bad passoword');
});
}

Where input is

var input = {
  Domain: true,
  DomainName: "DomainName",
  UserName: "DomainUserName",
  Password : "DomainUserpassword"
};

Sending GCM notifications

For sending PUSH notifications via GCM service we integrated node-gcm package.

Sending APN notifications

For sending push notification to iOS platform we used apnagent package. A good blog post on how to integrate sending messages/notifications to APN service is written on https://blog.engineyard.com/2013/developing-ios-push-notifications-nodejs. There is one detail you should be careful when coding: if you are reading your pfx certificate

var join = require('path').join
  , pfx = join(__dirname, '../_certs/pfx.p12');
  ...
  ...
  agent.set('pfx file', pfx);

you should be careful to write pfx file and not pfx (buffer) or anything else.