Search code examples
sslhttpsmeteorcertificateself-signed

Meteor - how to submit an SSL certificate for *client* authentication in Meteor using a POST request?


EDIT: Updated Question here -

Based on @ DoctorPangloss answer, I changed this to a server method. Below is the code. I'm not sure whether this is the right way to make the API call. The data returned is null from the server. Output is below the code.

Meteor.startup(function() {
  var cert = Assets.getText('client-2048.p12');

  Meteor.http.post(
    "https://identitysso.betfair.com/api/certlogin", 
    { 
      headers: 
      { 
        "X-Application": "njhR7Q3ELfAZENlr",
        "Content-Type": "application/x-www-form-urlencoded" 
      },
      params: {
        "username": "xxxx",
        "password": "xxxx",
        "pkcs12": cert,
        "password": "xxxx"
      }
    },
    function(error, result) 
    {
      console.log(result);
    }
    );
});

Output after the API login call:

I20140529-16:38:37.220(5.5)? { statusCode: 404,
I20140529-16:38:37.220(5.5)?   content: ' Lot of HTML data here   '                
I20140529-16:38:37.221(5.5)?   headers: 
I20140529-16:38:37.221(5.5)?    { 'set-cookie': 
I20140529-16:38:37.221(5.5)?       [ 'wsid=7a5e0a81-e720-11e3-9af8-066b01d132ba; Domain=.betfair.com; Path=/',
I20140529-16:38:37.221(5.5)?         'vid=7a5e0a82-e720-11e3-9af8-066b01d132ba; Domain=.betfair.com; Expires=Sun, 26-May-2024 11:00:42 GMT; Path=/' ],
I20140529-16:38:37.222(5.5)?      'cache-control': 'no-cache,must-revalidate,no-store',
I20140529-16:38:37.222(5.5)?      expires: '-1',
I20140529-16:38:37.222(5.5)?      pragma: 'no-cache',
I20140529-16:38:37.222(5.5)?      'x-frame-options': 'DENY',
I20140529-16:38:37.222(5.5)?      'content-type': 'text/html;charset=UTF-8',
I20140529-16:38:37.222(5.5)?      'content-language': 'en-GB',
I20140529-16:38:37.222(5.5)?      'transfer-encoding': 'chunked',
I20140529-16:38:37.223(5.5)?      date: 'Thu, 29 May 2014 11:00:42 GMT',
I20140529-16:38:37.223(5.5)?      vary: 'Accept-Encoding' },
I20140529-16:38:37.223(5.5)?   data: null }

Previous question:

A self-signed SSL cert needs to be sent to an external server to login. This is on localhost.

I'm sure I checked enough for an existing answer, but only this question comes close - Checking clients certificate in Meteor.

I'm trying to achieve BetFair's Non-interactive (Bot) login: https://api.developer.betfair.com/services/webapps/docs/display/1smk3cen4v3lu3yomq5qye0ni/Non-Interactive+%28bot%29+login

Here's the testcode on the client for a simple button click event:

Template.postsList.events({
    'click .betfair': function() {
        HTTP.call("POST", "https://identitysso.betfair.com/api/certlogin",
            {headers: {
                'X-Application' : 'njhR7Q3ELfAZENlr',
                'Accept': 'application/json',
                'Content-type' : 'application/x-www-form-urlencoded'
            },
            params: {
                'username': 'xxxxxx',
                'password': 'xxxxxx'
            },
            options: {
                cert: fs.readFileSync('/Users/mmapp/client-2048.crt'),
                requestCert: false,
                rejectUnauthorized: false
            }
        },
        function (error, result) {
            if (!error) {
                console.log("Looks like its logging in...");
            }
        });
    }
});

I'm aware of the possible problems:

  • fs.readFileSync only works in npm/node.js?
  • Meteor needs something like nginx to handle SSL?
  • Should the above code be running from the server block of meteor?
  • Should Npm.require("fs") be used somewhere?

How do we achieve this login process in Meteor? I'm a beginner and any help would be greatly appreciated! Thanks.


Solution

    1. fs.readFileSync works neither in client or server. Checkout Assets.getText to read a certificate from the server.
    2. If you want a client to provide a cert to BetFair, prompt for the file using an input and read the text data with Javascript. http://www.html5rocks.com/en/tutorials/file/dndfiles/ has a good overview on ways to read files in the browser. Note, some certificates have funky permissions to prevent you from, e.g., uploading the certificate to some browser.

    You can process the result as follows:

    function (error, result) {
      // From their website, the result is something like {"sessionToken":"Zx8i4oigut5nc+l4L8qFb0DSxG+mwLn2t0AMGFxjrMJI=","loginStatus":"SUCCESS"}
       if (result.data.loginStatus === "SUCCESS") {
         Session.set("botFairSessionToken", result.data.sessionToken);
       }
    }
    

    Depending on what you want to do, you should probably do this call on an Accounts.onCreateUser handler on the server. Instead of Session.set use Meteor.users.update({_id: this.userId}, {$set: {"services.botFair.sessionToken": result.data.sessionToken}});

    // Handler for a call inside a server method body:
    function (error, result) {
      // From their website, the result is something like {"sessionToken":"Zx8i4oigut5nc+l4L8qFb0DSxG+mwLn2t0AMGFxjrMJI=","loginStatus":"SUCCESS"}
       if (result.data.loginStatus === "SUCCESS") {
         Meteor.users.update({_id: this.userId},
                 {$set: {"services.botFair.sessionToken": result.data.sessionToken}})
       }
    }