summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorilotterytea <iltsu@alright.party>2025-05-06 00:56:04 +0500
committerilotterytea <iltsu@alright.party>2025-05-06 01:02:22 +0500
commit7cc2534f9183bb3116b19ffca52789f1f50900f7 (patch)
treea03b240d83b03e3d925061640fdc90084c2c4b18
parent91efe9a465df0a6647fbb0f7c5643be89cdcc7e1 (diff)
feat: account registration and login
-rw-r--r--public/account/index.php4
-rw-r--r--public/account/login/index.php80
-rw-r--r--public/account/login/twitch.php2
-rw-r--r--public/account/register.php111
-rw-r--r--public/static/style.css7
-rw-r--r--src/config.sample.php5
6 files changed, 191 insertions, 18 deletions
diff --git a/public/account/index.php b/public/account/index.php
index ff0b745..8ecce33 100644
--- a/public/account/index.php
+++ b/public/account/index.php
@@ -16,7 +16,7 @@ if (!isset($_SESSION["user_id"], $_SESSION["user_name"])) {
if ($_SERVER['REQUEST_METHOD'] == "POST") {
$db = new PDO(DB_URL, DB_USER, DB_PASS);
- $username = str_safe($_POST["username"] ?? "", ACCOUNT_USERNAME_MAX_LENGTH);
+ $username = str_safe($_POST["username"] ?? "", ACCOUNT_USERNAME_LENGTH[1]);
if (!empty($username) && $username != $_SESSION["user_name"]) {
if (!preg_match(ACCOUNT_USERNAME_REGEX, $username)) {
@@ -126,7 +126,7 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") {
username.addEventListener("input", (e) => {
const regex = <?php echo ACCOUNT_USERNAME_REGEX ?>;
- if (regex.test(e.target.value) && e.target.value.length <= <?php echo ACCOUNT_USERNAME_MAX_LENGTH ?>) {
+ if (regex.test(e.target.value) && e.target.value.length <= <?php echo ACCOUNT_USERNAME_LENGTH[1] ?>) {
validUsername = e.target.value;
} else {
e.target.value = validUsername;
diff --git a/public/account/login/index.php b/public/account/login/index.php
index 4eb37ae..7c562d1 100644
--- a/public/account/login/index.php
+++ b/public/account/login/index.php
@@ -1,16 +1,44 @@
<?php
include "../../../src/accounts.php";
-authorize_user();
+
+if (authorize_user()) {
+ header("Location: /account");
+ exit;
+}
include "../../../src/partials.php";
include_once "../../../src/config.php";
include_once "../../../src/alert.php";
+include_once "../../../src/utils.php";
-if (!ACCOUNT_REGISTRATION_ENABLE) {
- generate_alert("/404.php", "Account registration is disabled", 403);
- exit;
-}
+if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ if (!isset($_POST["username"], $_POST["password"])) {
+ generate_alert("/account/login", "Not enough POST fields");
+ exit;
+ }
+ $username = $_POST["username"];
+ $password = $_POST["password"];
+ $remember = intval($_POST["remember"] ?? "0") != 0;
+
+ $db = new PDO(DB_URL, DB_USER, DB_PASS);
+ $stmt = $db->prepare("SELECT secret_key, password FROM users WHERE username = ? AND password IS NOT NULL");
+ $stmt->execute([$username]);
+
+ if ($row = $stmt->fetch()) {
+ if (password_verify($password, $row["password"])) {
+ setcookie("secret_key", $row["secret_key"], $remember ? (time() + ACCOUNT_COOKIE_MAX_LIFETIME) : 0, "/");
+ header("Location: /account");
+ exit;
+ } else {
+ generate_alert("/account/login", "Passwords do not match!", 403);
+ exit;
+ }
+ } else {
+ generate_alert("/account/login", "User not found or is not accessable", 404);
+ exit;
+ }
+}
?>
<html>
@@ -25,21 +53,45 @@ if (!ACCOUNT_REGISTRATION_ENABLE) {
<div class="container">
<div class="wrapper">
<?php html_navigation_bar(); ?>
-
- <section class="content">
- <section class="box" style="width: 400px;">
+ <section class="content" style="width: 400px;">
+ <?php display_alert() ?>
+ <section class="box">
<div class="box navtab">
<p>Log in to <?php echo INSTANCE_NAME ?></p>
</div>
<div class="box content">
- <?php if (TWITCH_REGISTRATION_ENABLE): ?>
- <form action="/account/login/twitch.php" method="GET">
- <button type="submit" class="purple" style="padding:8px 24px; font-size: 18px;">Login with
- Twitch</button>
- </form>
- <?php endif; ?>
+ <form action="/account/login" method="post">
+ <div>
+ <label for="username">Username</label>
+ <input type="text" name="username" id="form-username" required>
+ </div>
+ <div>
+ <label for="password">Password</label>
+ <input type="password" name="password" id="form-password" required>
+ </div>
+ <div>
+ <input type="checkbox" name="remember" value="1" id="form-remember">
+ <label for="remember" class="inline">Remember me</label>
+ </div>
+ <div>
+ <button type="submit">Log in</button>
+ <?php if (ACCOUNT_REGISTRATION_ENABLE): ?>
+ <a href="/account/register.php">Register</a>
+ <?php endif; ?>
+ </div>
+ </form>
</div>
</section>
+
+ <?php if (TWITCH_REGISTRATION_ENABLE): ?>
+ <section class="box column">
+ <a href="/account/login/twitch.php" class="button purple"
+ style="padding:8px 24px; font-size: 18px;">Login with Twitch</a>
+ <p style="font-size: 12px;">Logging in via Twitch gives you the ability to use
+ <?php echo INSTANCE_NAME ?> emotes in your Twitch chat.
+ </p>
+ </section>
+ <?php endif; ?>
</section>
</div>
</div>
diff --git a/public/account/login/twitch.php b/public/account/login/twitch.php
index 05093cd..e3fe57a 100644
--- a/public/account/login/twitch.php
+++ b/public/account/login/twitch.php
@@ -129,7 +129,7 @@ if ($row = $stmt->fetch()) {
$_SESSION["user_id"] = $user_id;
$_SESSION["user_name"] = $user_name;
-setcookie("secret_key", $user_secret_key, time() + 86400 * 30, "/");
+setcookie("secret_key", $user_secret_key, time() + ACCOUNT_COOKIE_MAX_LIFETIME, "/");
$db = null;
diff --git a/public/account/register.php b/public/account/register.php
new file mode 100644
index 0000000..1da89a0
--- /dev/null
+++ b/public/account/register.php
@@ -0,0 +1,111 @@
+<?php
+include "../../src/accounts.php";
+include_once "../../src/alert.php";
+
+if (authorize_user()) {
+ header("Location: /account");
+ exit;
+}
+
+if (!ACCOUNT_REGISTRATION_ENABLE) {
+ generate_alert("/404.php", "Account registration is disabled", 403);
+ exit;
+}
+
+include "../../src/partials.php";
+include_once "../../src/config.php";
+include_once "../../src/utils.php";
+
+if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ if (!isset($_POST["username"], $_POST["password"])) {
+ generate_alert("/account/register.php", "Not enough POST fields");
+ exit;
+ }
+
+ $username = $_POST["username"];
+ $username_length = strlen($username);
+ if (ACCOUNT_USERNAME_LENGTH[0] > $username_length || $username_length > ACCOUNT_USERNAME_LENGTH[1]) {
+ generate_alert("/account/register.php", sprintf("Username must be between %d-%d characters long", ACCOUNT_USERNAME_LENGTH[0], ACCOUNT_USERNAME_LENGTH[1]));
+ exit;
+ }
+
+ if (!preg_match(ACCOUNT_USERNAME_REGEX, $username)) {
+ generate_alert("/account/register.php", "Bad username");
+ exit;
+ }
+
+ $password = $_POST["password"];
+ if (ACCOUNT_PASSWORD_MIN_LENGTH > strlen($password)) {
+ generate_alert("/account/register.php", "Password must be at least " . ACCOUNT_PASSWORD_MIN_LENGTH . " characters");
+ exit;
+ }
+
+ $db = new PDO(DB_URL, DB_USER, DB_PASS);
+
+ $stmt = $db->prepare("SELECT id FROM users WHERE username = ?");
+ $stmt->execute([$username]);
+
+ if ($stmt->rowCount() != 0) {
+ generate_alert("/account/register.php", "The username has already been taken");
+ exit;
+ }
+
+ $secret_key = generate_random_string(ACCOUNT_SECRET_KEY_LENGTH);
+ $password = password_hash($password, PASSWORD_DEFAULT);
+
+ $id = bin2hex(random_bytes(16));
+
+ $stmt = $db->prepare("INSERT INTO users(id, username, password, secret_key) VALUES (?, ?, ?, ?)");
+ $stmt->execute([$id, $username, $password, $secret_key]);
+
+ setcookie("secret_key", $secret_key, time() + ACCOUNT_COOKIE_MAX_LIFETIME, "/");
+ header("Location: /account");
+ exit;
+}
+?>
+
+<html>
+
+<head>
+ <title>Register an account - <?php echo INSTANCE_NAME ?></title>
+ <link rel="stylesheet" href="/static/style.css">
+ <link rel="shortcut icon" href="/static/favicon.ico" type="image/x-icon">
+</head>
+
+<body>
+ <div class="container">
+ <div class="wrapper">
+ <?php html_navigation_bar(); ?>
+
+ <section class="content" style="width: 400px;">
+ <?php display_alert() ?>
+ <section class="box">
+ <div class="box navtab">
+ <p>Register an account in <?php echo INSTANCE_NAME ?></p>
+ </div>
+ <div class="box content">
+ <form action="/account/register.php" method="post">
+ <div>
+ <label for="username">Username</label>
+ <input type="text" name="username" id="form-username" required>
+ </div>
+ <div>
+ <label for="password">Password</label>
+ <input type="password" name="password" id="form-password" required>
+ </div>
+ <div>
+ <button type="submit">Register</button>
+ </div>
+ </form>
+ <p style="font-size: 12px;">
+ Since <?php echo INSTANCE_NAME ?> doesn't require email and password reset via email is
+ not supported, please remember your passwords!
+ </p>
+ </div>
+ </section>
+ </section>
+ </div>
+ </div>
+</body>
+
+</html> \ No newline at end of file
diff --git a/public/static/style.css b/public/static/style.css
index 06be5e1..ed1396c 100644
--- a/public/static/style.css
+++ b/public/static/style.css
@@ -73,6 +73,13 @@ form {
gap: 4px;
}
+label {
+ display: block;
+}
+
+label.inline {
+ display: inline;
+}
.container {
width: 100%;
diff --git a/src/config.sample.php b/src/config.sample.php
index f1c3a45..3181956 100644
--- a/src/config.sample.php
+++ b/src/config.sample.php
@@ -38,8 +38,11 @@ define("REPORTS_ENABLE", true); // Enable emote, user reports.
// ACCOUNTS
define("ACCOUNT_REGISTRATION_ENABLE", true); // Enable account registration.
+define("ACCOUNT_COOKIE_MAX_LIFETIME", 86400 * 30); // Remember user for a month.
define("ACCOUNT_USERNAME_REGEX", "/^[A-Za-z0-9_]+$/"); // RegEx filter for account usernames.
-define("ACCOUNT_USERNAME_MAX_LENGTH", 20); // Max length for account usernames.
+define("ACCOUNT_USERNAME_LENGTH", [2, 20]); // [Min, Max] length for account usernames.
+define("ACCOUNT_PASSWORD_MIN_LENGTH", 10); // Minimal length for passwords.
+define("ACCOUNT_SECRET_KEY_LENGTH", 32); // The length for secret keys.
define("ACCOUNT_PFP_MAX_SIZE", [128, 128]); // Max dimensions for account pictures.
define("ACCOUNT_BANNER_MAX_SIZE", [1920, 1080]); // Max dimensions for account banners.