diff options
| author | ilotterytea <iltsu@alright.party> | 2025-05-06 00:56:04 +0500 |
|---|---|---|
| committer | ilotterytea <iltsu@alright.party> | 2025-05-06 01:02:22 +0500 |
| commit | 7cc2534f9183bb3116b19ffca52789f1f50900f7 (patch) | |
| tree | a03b240d83b03e3d925061640fdc90084c2c4b18 | |
| parent | 91efe9a465df0a6647fbb0f7c5643be89cdcc7e1 (diff) | |
feat: account registration and login
| -rw-r--r-- | public/account/index.php | 4 | ||||
| -rw-r--r-- | public/account/login/index.php | 80 | ||||
| -rw-r--r-- | public/account/login/twitch.php | 2 | ||||
| -rw-r--r-- | public/account/register.php | 111 | ||||
| -rw-r--r-- | public/static/style.css | 7 | ||||
| -rw-r--r-- | src/config.sample.php | 5 |
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. |
