Skip to content

Commit

Permalink
license keys
Browse files Browse the repository at this point in the history
  • Loading branch information
reZach committed Apr 21, 2021
1 parent 89a0709 commit 3bf6c84
Show file tree
Hide file tree
Showing 8 changed files with 19,939 additions and 11,926 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,8 @@ dist/

# Logfile specific for development builds
dev-scripts/webpack-dev-server.log
dev-scripts/webpack-dev-server-error.log
dev-scripts/webpack-dev-server-error.log

# License-specific files
license.data
public.key
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Built-in to this template are a number of popular frameworks already wired up to
- [Context menu](https://github.com/reZach/secure-electron-context-menu) (supports custom context menus)
- [Electron builder](https://www.electron.build/) (for packaging up your app)
- [Easy redux undo](https://github.com/reZach/easy-redux-undo) (for undo/redoing your redux actions)

- [License key validation](https://github.com/reZach/secure-electron-license-keys) (for validating a user has the proper license to use your app) **new!**

## Roadmap
There are a number of additions that I'd like to implement in this repository, namely more release-focused enhancements and test suites, but those are lower priority (but I welcome PRs!).
Expand Down
11 changes: 10 additions & 1 deletion app/electron/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const {
default: installExtension,
REDUX_DEVTOOLS,
REACT_DEVELOPER_TOOLS
} = require('electron-devtools-installer');
} = require("electron-devtools-installer");
const SecureElectronLicenseKeys = require("secure-electron-license-keys");
const Protocol = require("./protocol");
const MenuBuilder = require("./menu");
const i18nextBackend = require("i18next-electron-fs-backend");
Expand All @@ -19,6 +20,7 @@ const Store = require("secure-electron-store").default;
const ContextMenu = require("secure-electron-context-menu").default;
const path = require("path");
const fs = require("fs");
const crypto = require("crypto");
const isDev = process.env.NODE_ENV === "development";
const port = 40992; // Hardcoded; needs to match webpack.development.js and package.json
const selfHost = `http://localhost:${port}`;
Expand Down Expand Up @@ -93,6 +95,12 @@ async function createWindow() {
}]
});

// Setup bindings for offline license verification
SecureElectronLicenseKeys.mainBindings(ipcMain, win, fs, crypto, {
root: process.cwd(),
version: app.getVersion()
});

// Load app
if (isDev) {
win.loadURL(selfHost);
Expand Down Expand Up @@ -197,6 +205,7 @@ app.on("window-all-closed", () => {
} else {
i18nextBackend.clearMainBindings(ipcMain);
ContextMenu.clearMainBindings(ipcMain);
SecureElectronLicenseKeys.clearMainBindings(ipcMain);
}
});

Expand Down
4 changes: 3 additions & 1 deletion app/electron/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const fs = require("fs");
const i18nextBackend = require("i18next-electron-fs-backend");
const Store = require("secure-electron-store").default;
const ContextMenu = require("secure-electron-context-menu").default;
const SecureElectronLicenseKeys = require("secure-electron-license-keys");

// Create the electron store to be made available in the renderer process
const store = new Store();
Expand All @@ -12,5 +13,6 @@ const store = new Store();
contextBridge.exposeInMainWorld("api", {
i18nextElectronBackend: i18nextBackend.preloadBindings(ipcRenderer),
store: store.preloadBindings(ipcRenderer, fs),
contextMenu: ContextMenu.preloadBindings(ipcRenderer)
contextMenu: ContextMenu.preloadBindings(ipcRenderer),
licenseKeys: SecureElectronLicenseKeys.preloadBindings(ipcRenderer)
});
143 changes: 143 additions & 0 deletions app/src/core/nav.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import React from "react";
import ROUTES from "Constants/routes";
import {
validateLicenseRequest,
validateLicenseResponse,
} from "secure-electron-license-keys";

class Nav extends React.Component {
constructor(props) {
Expand All @@ -8,18 +12,73 @@ class Nav extends React.Component {
this.history = props.history;
this.state = {
mobileMenuActive: false,
licenseModalActive: false,

// license-specific
licenseValid: false,
allowedMajorVersions: "",
allowedMinorVersions: "",
appVersion: "",
licenseExpiry: "",
};

this.toggleMenu = this.toggleMenu.bind(this);
this.toggleLicenseModal = this.toggleLicenseModal.bind(this);
this.navigate = this.navigate.bind(this);
}

componentWillUnmount() {
window.api.licenseKeys.clearRendererBindings();
}

componentDidMount() {
// Set up binding to listen when the license key is
// validated by the main process
const _ = this;

window.api.licenseKeys.onReceive(validateLicenseResponse, function (data) {
// If the license key/data is valid
if (data.success) {
// Here you would compare data.appVersion to
// data.major, data.minor and data.patch to
// ensure that the user's version of the app
// matches their license
_.setState({
licenseValid: true,
allowedMajorVersions: data.major,
allowedMinorVersions: data.minor,
allowedPatchVersions: data.patch,
appVersion: data.appVersion,
licenseExpiry: data.expire,
});
} else {
_.setState({
licenseValid: false,
});
}
});
}

toggleMenu(event) {
this.setState({
mobileMenuActive: !this.state.mobileMenuActive,
});
}

toggleLicenseModal(event) {
const previous = this.state.licenseModalActive;

// Only send license request if the modal
// is not already open
if (!previous) {
window.api.licenseKeys.send(validateLicenseRequest);
}

this.setState({
licenseModalActive: !this.state.licenseModalActive,
});
}

// Using a custom method to navigate because we
// need to close the mobile menu if we navigate to
// another page
Expand All @@ -34,6 +93,78 @@ class Nav extends React.Component {
);
}

renderLicenseModal() {
return (
<div
className={`modal ${this.state.licenseModalActive ? "is-active" : ""}`}>
<div className="modal-background"></div>
<div className="modal-content">
{this.state.licenseValid ? (
<div className="box">
The license key for this product has been validated and the
following versions of this app are allowed for your use:
<div>
<strong>Major versions:</strong>{" "}
{this.state.allowedMajorVersions} <br />
<strong>Minor versions:</strong>{" "}
{this.state.allowedMinorVersions} <br />
<strong>Patch versions:</strong>{" "}
{this.state.allowedPatchVersions} <br />
<strong>Expires on:</strong>{" "}
{!this.state.licenseExpiry
? "never!"
: this.state.licenseExpiry}{" "}
<br />(
<em>
App version:
{` v${this.state.appVersion.major}.${this.state.appVersion.minor}.${this.state.appVersion.patch}`}
</em>
)
<br />
</div>
</div>
) : (
<div className="box">
<div>The license key is not valid.</div>
<div>
If you'd like to create a license key, follow these steps:
<ol style={{ marginLeft: "30px" }}>
<li>
Install this package globally (
<strong>npm i secure-electron-license-keys-cli -g</strong>).
</li>
<li>
Run <strong>secure-electron-license-keys-cli</strong>.
</li>
<li>
Copy <strong>public.key</strong> and{" "}
<strong>license.data</strong> into the <em>root</em> folder
of this app.
</li>
<li>
Re-run this app (ie. <strong>npm run dev</strong>).
</li>
<li>
If you'd like to further customize your license keys, copy
this link into your browser:{" "}
<a href="https://github.com/reZach/secure-electron-license-keys-cli">
https://github.com/reZach/secure-electron-license-keys-cli
</a>
.
</li>
</ol>
</div>
</div>
)}
</div>
<button
className="modal-close is-large"
aria-label="close"
onClick={this.toggleLicenseModal}></button>
</div>
);
}

render() {
return (
<nav
Expand Down Expand Up @@ -100,6 +231,18 @@ class Nav extends React.Component {
</div>
</div>
</div>
{this.renderLicenseModal()}
<div className="navbar-end">
<div className="navbar-item">
<div className="buttons">
<a
className="button is-light"
onClick={this.toggleLicenseModal}>
Check license
</a>
</div>
</div>
</div>
</div>
</nav>
);
Expand Down
3 changes: 3 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,8 @@ No, but you can start with this template and follow the steps [outlined here](ht
## Can I use `yarn` to install dependencies?
Yes, but you'll have to follow [a few steps](https://github.com/reZach/secure-electron-template/issues/62) to get it working.

## How do I set up my own license keys?
Please refer to [these instructions](https://github.com/reZach/secure-electron-license-keys) first. If you have further questions, you may post a question in the appropriate repo.

#### Question not answered?
Please [post an issue](https://github.com/reZach/secure-electron-template/issues/new) and we will add to this page with questions that you have!
Loading

0 comments on commit 3bf6c84

Please sign in to comment.