Use node-forge for PKCS#12 support. Remove need to write keys to file system.
[xtuple] / node-datasource / routes / generate_oauth_key.js
1 /*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
2 regexp:true, undef:true, strict:true, trailing:true, white:true */
3 /*global X:true, _:true, SYS:true */
4
5 // https://localhost/dev/oauth/generate-key?id=7
6
7 (function () {
8   "use strict";
9
10   var ursa = require("ursa"),
11     exec = require("child_process").exec,
12     forge = require("node-forge"),
13     spawn = require("child_process").spawn,
14     async = require("async"),
15     path = require("path"),
16     fs = require("fs");
17
18   /**
19     Fetch the requested oauth2client model, validate the request,
20     generate a keypair whose public key will be saved with the
21     model and whose private key is returned to the browser.
22    */
23   exports.generateKey = function (req, res) {
24     var clientModel = new SYS.Oauth2client(),
25       id = req.query.id,
26       // generic error function for both the fetch and the save
27       error = function (model, err) {
28         console.log("oauth2client error ", arguments);
29         res.send({isError: true, error: err});
30       },
31       genKey = function (model, result) {
32         /**
33           * This is REALLY slow in pure javascript. ursa is much faster.
34           * @See: https://github.com/digitalbazaar/forge/issues/125
35         forge.pki.rsa.generateKeyPair({bits: 2048, workers: 2}, function(err, keypair) {
36           if (err) {
37             res.send({isError: true, message: "Error generating keypair: " + err.message, error: err});
38             return;
39           }
40
41           console.log("1.2: ", new Date().getTime());
42           fetchSuccess(model, result, keypair);
43         });
44         */
45
46         // Use ursa for the key gen and then convert to forge's format.
47         var keypair = ursa.generatePrivateKey();
48         var keys = {
49           privateKey: forge.pki.privateKeyFromPem(keypair.toPrivatePem().toString()),
50           publicKey: forge.pki.publicKeyFromPem(keypair.toPublicPem().toString())
51         };
52
53         fetchSuccess(model, result, keys);
54       },
55       sendP12 = function (keys) {
56         // It's possible and much easier to generate the p12 file without a
57         // cert. This example shows how to generate a cert if we actually need
58         // to, but OAuth is working without.
59         // @see: https://github.com/digitalbazaar/forge/blob/master/tests/nodejs-create-pkcs12.js#L11
60         //var p12Asn1 = forge.pkcs12.toPkcs12Asn1(keys.privateKey, [cert], 'notasecret'),
61         var p12Asn1 = forge.pkcs12.toPkcs12Asn1(keys.privateKey, null, 'notasecret'),
62           p12Der = forge.asn1.toDer(p12Asn1).getBytes(),
63           buffer = new Buffer(p12Der, 'binary');
64
65         res.attachment(clientModel.get('clientName') + '.p12');
66         res.send(new Buffer(buffer, 'base64'));
67       },
68       fetchSuccess = function (model, result, keys) {
69         var publicKey = forge.pki.publicKeyToPem(keys.publicKey),
70           saveSuccess = function (model, result) {
71             sendP12(keys);
72           };
73
74         // Cursory validation: this should be a jwt bearer and the
75         // public key field should not have already been set.
76         if (clientModel.get("clientType" !== "jwt bearer") ||
77             clientModel.get("clientX509PubCert")) {
78           res.send({isError: true, message: "Invalid request"});
79           return;
80         }
81
82         clientModel.set("clientX509PubCert", publicKey);
83         clientModel.save(null, {
84           error: error,
85           username: req.session.passport.user.username,
86           database: req.session.passport.user.organization,
87           success: saveSuccess
88         });
89       };
90
91     clientModel.fetch({
92       id: id,
93       username: req.session.passport.user.username,
94       database: req.session.passport.user.organization,
95       error: error,
96       success: genKey
97     });
98
99   };
100 }());