This commit is contained in:
Kim Ravn Hansen
2025-09-14 13:04:48 +02:00
parent 8c196bb6a1
commit ed91a7f2f7
43 changed files with 2279 additions and 1727 deletions

View File

@@ -0,0 +1,37 @@
import { PasswordPrompt } from "./passwordPrompt.js";
import { Player } from "../../models/player.js";
import { Scene } from "../scene.js";
import { UsernamePrompt } from "./usernamePrompt.js";
/** @property {Session} session */
export class AuthenticationScene extends Scene {
introText = [
"= Welcome", //
];
/** @type {Player} */
player;
onReady() {
// current prompt
this.doPrompt(new UsernamePrompt(this));
}
/** @param {Player} player */
usernameAccepted(player) {
this.player = player;
this.doPrompt(new PasswordPrompt(this));
}
passwordAccepted() {
this.player.loggedIn = true;
this.session.player = this.player;
if (this.player.admin) {
this.session.setScene("new AdminJustLoggedInScene");
} else {
this.session.setScene("new JustLoggedInScene");
}
}
}

View File

@@ -0,0 +1,80 @@
import { Prompt } from "../prompt.js";
import * as security from "../../utils/security.js";
import { Config } from "../../config.js";
import { context } from "../../utils/messages.js";
export class PasswordPrompt extends Prompt {
//
promptText = "Please enter your password";
//
// Let the client know that we're asking for a password
// so it can set <input type="password">
promptOptions = { password: true };
get player() {
return this.scene.player;
}
onReply(text) {
//
// Check of the password is sane. This is both bad from a security point
// of view, and technically not necessary as insane passwords couldn't
// reside in the player lists. However, let's save some CPU cycles on
// not hashing an insane password 1000+ times.
// This is technically bad practice, but since this is just a game,
// do it anyway.
if (!security.isPasswordSane(text)) {
this.sendError("Insane password");
this.execute();
return;
}
//
// Block users who enter bad passwords too many times.
if (this.player.failedPasswordsSinceLastLogin > Config.maxFailedLogins) {
this.blockedUntil = Date.now() + Config.accountLockoutSeconds;
this.calamity("You have been locked out for too many failed password attempts, come back later");
return;
}
//
// Handle blocked users.
// They don't even get to have their password verified.
if (this.player.blockedUntil > Date.now()) {
//
// Try to re-login too soon, and your lockout lasts longer.
this.blockedUntil += Config.accountLockoutSeconds;
this.calamity("You have been locked out for too many failed password attempts, come back later");
return;
}
//
// Verify the password against the hash we've stored.
if (!security.verifyPassword(text, this.player.passwordHash)) {
this.sendError("Incorrect password!");
this.player.failedPasswordsSinceLastLogin++;
this.session.sendDebug(`Failed login attempt #${this.player.failedPasswordsSinceLastLogin}`);
this.execute();
return;
}
this.player.lastSucessfulLoginAt = new Date();
this.player.failedPasswordsSinceLastLogin = 0;
//
// We do not allow a player to be logged in more than once!
if (this.player.loggedIn) {
this.calamity("This player is already logged in");
return;
}
this.scene.passwordAccepted();
//
// Password was correct, go to main game
this.session.setState(new JustLoggedInState(this.session));
}
}

View File

@@ -0,0 +1,64 @@
import { Player } from "../../models/player.js";
import { Prompt } from "../prompt.js";
import * as security from "../../utils/security.js";
import { context } from "../../utils/messages.js";
import { gGame } from "../../models/globals.js";
import { PlayerCreationScene } from "../playerCreation/playerCreationSene.js";
import { Config } from "../../config.js";
export class UsernamePrompt extends Prompt {
//
promptText = [
"Please enter your username:", //
"(((type *:create* if you want to create a new user)))", //
];
//
// When player types :help
helpText = [
"This is where you log in.",
"If you don't already have a player profile on this server, you can type *:create* to create one",
];
//
// Let the client know that we're asking for a username
promptOptions = { username: true };
//
// User entered ":create"
onColon_create() {
// User creation scene.
this.scene.session.setScene(new PlayerCreationScene(this.scene));
}
//
// User replied to our prompt
onReply(text) {
//
// do basic syntax checks on usernames
if (!security.isUsernameSane(text)) {
console.info("Someone entered insane username: '%s'", text);
this.sendError("Incorrect username, try again");
this.execute();
return;
}
//
// try and fetch the player object from the game
const player = gGame.getPlayer(text);
//
// handle invalid username
if (!player) {
console.info("Someone entered incorrect username: '%s'", text);
this.sendError("Incorrect username, try again");
this.execute();
return;
}
//
// Tell daddy that we're done
this.scene.usernameAccepted(player);
}
}