Skip to content

Commit 5350dd4

Browse files
authored
Merge pull request #3 from bartbutenaers/master
Keys in credentials & button to generate keypair
2 parents 1f7b251 + 3578be3 commit 5350dd4

File tree

3 files changed

+118
-14
lines changed

3 files changed

+118
-14
lines changed

vapid-configuration.html

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,63 @@
33
category: 'config',
44
defaults: {
55
subject: { value: "", required: true },
6-
publicKey: { value: "", required: true },
7-
privateKey: { value: "", required: true },
6+
// The below 3 key fields are not used anymore (i.e. replaced by credentials) but we cannot
7+
// remove them here, otherwise the keys of old existing nodes would be lost ...
8+
publicKey: { value: ""},
9+
privateKey: { value: ""},
810
gcmApiKey: { value: "" },
911
name: { value: "" }
1012
},
13+
credentials: {
14+
// TODO how make credentials required??
15+
publicKey2: {type:"password"},
16+
privateKey2: {type: "password"},
17+
gcmApiKey2: {type:"password"}
18+
},
1119
label: function () {
1220
return this.name || this.subject;
21+
},
22+
oneditprepare: function () {
23+
var node = this;
24+
25+
// For old nodes (without credentials), the (unsecure) keys should be converted to credentials. Some remarks:
26+
// - The old html screen elements should remain available, in order to keep the old node values.
27+
// - The new keys have postfix '2', otherwise Node-RED binds the html screen element to both old and new node fields.
28+
// - We will manipulate the html screen element values, so Node-RED will automatically sync the node values (at 'Done' or 'Cancel').
29+
if (node.publicKey) {
30+
$('#node-config-input-publicKey').val("");
31+
$('#node-config-input-publicKey2').val(node.publicKey);
32+
}
33+
if (node.privateKey) {
34+
$('#node-config-input-privateKey').val("");
35+
$('#node-config-input-privateKey2').val(node.privateKey);
36+
}
37+
if (node.gcmApiKey) {
38+
$('#node-config-input-gcmApiKey').val("");
39+
$('#node-config-input-gcmApiKey2').val(node.gcmApiKey);
40+
}
41+
42+
$("#node-input-generateKeyPair").click(function () {
43+
if ($("#node-config-input-publicKey2").val() || $("#node-config-input-privateKey2").val()) {
44+
if (!confirm("The current keypair will be overwritten! Are you sure to continue?")) {
45+
// The user has clicked the 'Cancel' button
46+
return;
47+
}
48+
}
49+
50+
// Ask the server side flow to generate a new key pair
51+
$.getJSON('vapid_configuration/generate_key_pair', function(jsonData) {
52+
// Show the new keys on the config screen
53+
$("#node-config-input-publicKey2").val(jsonData.publicKey);
54+
$("#node-config-input-privateKey2").val(jsonData.privateKey);
55+
56+
// Make sure the validators are being triggerd, otherwise the red border will remain around the input fields
57+
$("#node-config-input-publicKey2").change();
58+
$("#node-config-input-privateKey2").change();
59+
}).error(function() {
60+
RED.notify("Cannot create VAPID key pair. See Node-RED log for more details...", "error");
61+
});
62+
});
1363
}
1464
});
1565

@@ -21,16 +71,26 @@
2171
<input type="text" id="node-config-input-subject" placeholder="This must be either a URL or a 'mailto:' address.">
2272
</div>
2373
<div class="form-row">
24-
<label for="node-config-input-publicKey"><i class="icon-tag"></i> Public Key</label>
25-
<input type="text" id="node-config-input-publicKey" placeholder="The public VAPID key">
74+
<label>&nbsp;</label>
75+
<button id="node-input-generateKeyPair"><i class="fa fa-exchange"></i> Generate VAPID keypair</button>
76+
</div>
77+
<div class="form-row" hidden>
78+
<!-- Unused hidden fields to bind the old (unsecure) keys in the node instance -->
79+
<input type="text" id="node-config-input-publicKey">
80+
<input type="text" id="node-config-input-privateKey">
81+
<input type="text" id="node-config-input-gcmApiKey">
82+
</div>
83+
<div class="form-row">
84+
<label for="node-config-input-publicKey2"><i class="icon-tag"></i> Public Key</label>
85+
<input type="password" id="node-config-input-publicKey2">
2686
</div>
2787
<div class="form-row">
28-
<label for="node-config-input-privateKey"><i class="icon-tag"></i> Private Key</label>
29-
<input type="text" id="node-config-input-privateKey" placeholder="The public VAPID key">
88+
<label for="node-config-input-privateKey2"><i class="icon-tag"></i> Private Key</label>
89+
<input type="password" id="node-config-input-privateKey2">
3090
</div>
3191
<div class="form-row">
32-
<label for="node-config-input-gcmApiKey"><i class="icon-tag"></i> GCM API Key (for older browsers)</label>
33-
<input type="text" id="node-config-input-gcmApiKey" placeholder="The API key to send with the GCM request">
92+
<label for="node-config-input-gcmApiKey2"><i class="icon-tag"></i> GCM API Key (for older browsers)</label>
93+
<input type="password" id="node-config-input-gcmApiKey2">
3494
</div>
3595
<div class="form-row">
3696
<label for="node-config-input-name"><i class="icon-tag"></i> Name</label>
@@ -41,4 +101,4 @@
41101
<script type="text/x-red" data-help-name="vapid-configuration">
42102
<p>Configuration for VAPID. You can generate the key pair using <code>generateVAPIDKeys()</code> method of <a href="https://www.npmjs.com/package/web-push" target="_blank">web-push</a> library or online here: <a href="https://web-push-codelab.glitch.me/" target="_blank">https://web-push-codelab.glitch.me</a>.</p>
43103
<p>For Chrome prior to version 52 and some old browsers, you're also still required to include a <code>gcm_sender_id</code> in your web app's manifest.json.</p>
44-
</script>
104+
</script>

vapid-configuration.js

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,51 @@
11
module.exports = function (RED) {
2+
const webpush = require('web-push');
3+
24
function VapidConfigurationNode (config) {
35
RED.nodes.createNode(this, config)
46
this.subject = config.subject
7+
// Keep the old key fields, to support old nodes (without credentials)
58
this.publicKey = config.publicKey
69
this.privateKey = config.privateKey
710
this.gcmApiKey = config.gcmApiKey
11+
12+
var node = this;
13+
14+
// Allow other nodes to access the secure node private credentials.
15+
// This is not good practice, but don't see a better approach since these nodes were designed originally without credentials ...
16+
this.getKeys = function () {
17+
// Use the new keys in the credentials, unless we are dealing with an old node (without credentials)
18+
return {
19+
publicKey : node.credentials.publicKey2 || node.publicKey,
20+
privateKey: node.credentials.privateKey2 || node.privateKey,
21+
gcmApiKey : node.credentials.gcmApiKey2 || node.gcmApiKey
22+
}
23+
}
824
}
9-
RED.nodes.registerType('vapid-configuration', VapidConfigurationNode)
25+
26+
RED.nodes.registerType("vapid-configuration", VapidConfigurationNode,{
27+
credentials: {
28+
publicKey2: {type: "password"},
29+
privateKey2: {type: "password"},
30+
gcmApiKey2: {type: "password"}
31+
}
32+
});
33+
34+
// Make the key pair generation available to the config screen (in the flow editor)
35+
RED.httpAdmin.get('/vapid_configuration/generate_key_pair', RED.auth.needsPermission('vapid-configuration.write'), async function(req, res){
36+
try {
37+
// Generate a VAPID keypair
38+
const vapidKeys = webpush.generateVAPIDKeys();
39+
40+
// Return public key and private key to the config screen (since they need to be stored in the node's credentials)
41+
res.json({
42+
publicKey: vapidKeys.publicKey,
43+
privateKey: vapidKeys.privateKey
44+
})
45+
}
46+
catch (err) {
47+
console.log("Error while generating VAPID keypair: " + err)
48+
res.status(500).json({error: 'Error while generating VAPID keypair'})
49+
}
50+
});
1051
}

web-push.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,19 @@ module.exports = function (RED) {
1616

1717
let options
1818
if (node.vapidConfiguration) {
19+
// Get the secure keys from the VAPID configuration credentials
20+
var keys = node.vapidConfiguration.getKeys();
21+
1922
options = {
2023
vapidDetails: {
2124
subject: node.vapidConfiguration.subject,
22-
publicKey: node.vapidConfiguration.publicKey,
23-
privateKey: node.vapidConfiguration.privateKey
25+
publicKey: keys.publicKey,
26+
privateKey: keys.privateKey
2427
}
2528
}
2629

27-
if (node.vapidConfiguration.gcmApiKey) {
28-
options['gcmAPIKey'] = node.vapidConfiguration.gcmApiKey
30+
if (keys.gcmApiKey) {
31+
options['gcmAPIKey'] = keys.gcmApiKey
2932
}
3033
}
3134

0 commit comments

Comments
 (0)