Files
factorio-clusterio/plugins/player_auth/instance.js
T
Hornwitser 72d0994ca6 Add Player Auth plugin (#368)
Add authentication plugin that does a simple challenge response protocol
to verify that the player trying to log in is who they claim to be.  The
process goes as follows:

1. A player code is generated for a player that wants to log in and is
   displayed in-game.
2. The player code is input into the web interface and the master
   generates a verification code as a challenge back along with a token.
3. The player inputs the verification code in-game and is logged in to
   the web interface in doing so.

To do the actual login the web interface polls a verify endpoint every 2
seconds until the code has either been verified or expired.  The
challenge comes from being unable to control the verification code and
having to input it as a player logging into one of the servers in order
to login.

Some additional steps are taken in order to increase the difficulty for
an attacker to subvert the challenge:

- The login process must be started from in-game.
- Generated codes are only valid for a limited timespan which is
  configurable and 2 minutes by default.
- The verification code comes with a token that only works with one
  player code and verification code combo and must be provided to pass
  validation.
2021-03-17 18:48:22 +01:00

56 lines
1.5 KiB
JavaScript

/**
* @module
*/
"use strict";
const libPlugin = require("@clusterio/lib/plugin");
const libLuaTools = require("@clusterio/lib/lua_tools");
class InstancePlugin extends libPlugin.BaseInstancePlugin {
async init() {
if (!this.instance.config.get("factorio.enable_save_patching")) {
throw new Error("player_auth plugin requires save patching.");
}
this.instance.server.on("ipc-player_auth", event => this.handleEvent(event).catch(
err => this.logger.error(`Error handling event:\n${err.stack}`)
));
}
async handleEvent(event) {
if (event.type === "open_dialog") {
if (!this.slave.connector.connected) {
await this.sendRcon(`/web-login error ${event.player} login is temporarily unavailable`);
return;
}
let response;
try {
response = await this.info.messages.fetchPlayerCode.send(this.instance, { player: event.player });
} catch (err) {
await this.sendRcon(`/web-login error ${event.player} ${err.message}`);
return;
}
await this.sendRcon(`/web-login open ${event.player} ${response.master_url} ${response.player_code}`);
} else if (event.type === "set_verify_code") {
try {
await this.info.messages.setVerifyCode.send(this.instance, {
player: event.player,
verify_code: event.verify_code,
});
} catch (err) {
await this.sendRcon(`/web-login error ${event.player} ${err.message}`);
return;
}
await this.sendRcon(`/web-login code_set ${event.player}`);
}
}
}
module.exports = {
InstancePlugin,
};