From 10702cef414eec6611086117565d565f8c35e8ee Mon Sep 17 00:00:00 2001
From: bye <bye@byecorps.com>
Date: Wed, 29 May 2024 18:33:57 +0100
Subject: [PATCH] i dont know whats in this commit hopefully it's good

---
 accounts_handler.php          | 34 ++++++++++++--
 admin.php                     |  5 ++
 admin_accounts.php            |  6 +--
 admin_create_token.php        | 86 ++++++++++++++++++++++++++++++++++
 api_handler.php               | 65 +++++++++++++++++++++-----
 assets/buttons/signinwith.svg | 88 +++++++++++++++++++++++++++++++++++
 head.php                      |  2 +-
 index.php                     |  2 +
 landing.php                   | 32 +++++++++++--
 login_external_basic.php      | 41 +++++++++++-----
 profile.php                   | 48 +------------------
 settings_account.php          | 79 +++++++++++++++++++++++++++++++
 signin.php                    |  2 +-
 styles/colours.css            |  6 +++
 styles/profiles.css           |  8 ++++
 styles/types.css              |  4 ++
 16 files changed, 426 insertions(+), 82 deletions(-)
 create mode 100644 admin_create_token.php
 create mode 100644 assets/buttons/signinwith.svg
 create mode 100644 settings_account.php

diff --git a/accounts_handler.php b/accounts_handler.php
index 34e6c0a..1ae09c3 100644
--- a/accounts_handler.php
+++ b/accounts_handler.php
@@ -30,7 +30,7 @@ function get_display_name($bcid, $use_bcid_fallback=true, $put_bcid_in_parenthes
 
 // Tokens so apps can get VERY BASIC information
 
-function generate_basic_access_token($bcid): array
+function generate_basic_access_token($bcid, $application_id=""): array
 {
     // Returns an access token, a refresh token and an expiry timestamp.
 
@@ -42,14 +42,42 @@ function generate_basic_access_token($bcid): array
 
 //    echo $access_token . ":" . $refresh_token;
 
+    if ($application_id) {
+        db_execute(
+            "INSERT INTO tokens (access_token, refresh_token, expiry, owner_id, application_id, permissions) VALUES (?,?,?,?,?, (1<<0 | 1<<1))",
+            [$access_token, $refresh_token, $expiry, $bcid, $application_id]
+        );
+    } else {
+        db_execute(
+            "INSERT INTO tokens (access_token, refresh_token, expiry, owner_id, permissions) VALUES (?,?,?,?, (1<<0 | 1<<1))",
+            [$access_token, $refresh_token, $expiry, $bcid]
+        );
+    }
+
+    return [
+        "access" => $access_token,
+        "refresh" => $refresh_token,
+        "expiry" => $expiry,
+        "id" => $bcid
+    ];
+}
+
+function generate_token($bcid, $application_id=null, $permissions=0): array {
+    $access_token = md5(uniqid(more_entropy: true).rand(1000000, 9999999));
+    $refresh_token = md5(uniqid("rfish").rand(1000000, 9999999));
+
+    $valid_time = 12; // in hours
+    $expiry = time() + ($valid_time * 60 * 60);
+
     db_execute(
-        "INSERT INTO tokens (access_token, refresh_token, expiry, owner_id) VALUES (?,?,?,?)",
-        [$access_token, $refresh_token, $expiry, $bcid]
+        "INSERT INTO tokens (access_token, refresh_token, expiry, owner_id, application_id, permissions, type) VALUES (?,?,?,?,?,?, 'oauth')",
+        [$access_token, $refresh_token, $expiry, $bcid, $application_id, $permissions]
     );
 
     return [
         "access" => $access_token,
         "refresh" => $refresh_token,
+        "permissions" => $permissions,
         "expiry" => $expiry,
         "id" => $bcid
     ];
diff --git a/admin.php b/admin.php
index d92d029..efb0b73 100644
--- a/admin.php
+++ b/admin.php
@@ -18,6 +18,11 @@
     </li>
 </ul>
 
+<h2>API</h2>
+<ul>
+    <li><a href="/admin/create/token">Token generator</a></li>
+</ul>
+
 <h2>Init</h2>
 <ul>
 	<li>
diff --git a/admin_accounts.php b/admin_accounts.php
index 3a73e80..b353838 100644
--- a/admin_accounts.php
+++ b/admin_accounts.php
@@ -21,9 +21,9 @@ $count = $count_req->fetchColumn();
 <ul>
     <?php
     foreach ($result as $row) {
-        echo "<li><pre>";
-        print_r($row);
-        echo "</pre><p><a href='/admin/signinas?id=".$row['id']."'>Sign in as ".$row['display_name']."</a></li>";
+        echo "<li>";
+        echo $row['id'];
+        echo "<p><a href='/admin/signinas?id=".$row['id']."'>Sign in as ".htmlspecialchars($row['display_name'])."</a></li>";
     }
     ?>
 </ul>
diff --git a/admin_create_token.php b/admin_create_token.php
new file mode 100644
index 0000000..6456f05
--- /dev/null
+++ b/admin_create_token.php
@@ -0,0 +1,86 @@
+<?php
+
+if ($_SERVER["REQUEST_METHOD"] == "POST") {
+    echo "<pre>";
+    print_r($_POST);
+    echo "</pre>";
+
+    $token = generate_token($_POST['owner'], $_POST['application'], $_POST['permissions']);
+
+    echo "<p>Created token. Access token: <code>". $token['access'] ."</code></p>";
+}
+
+?>
+
+<h1>Token generator</h1>
+
+<form method="post">
+    <div class="container">
+        <label for="owner">Token owner</label>
+        <select name="owner" required id="owner">
+            <?php
+            $users = db_query("SELECT * FROM accounts");
+            foreach ($users as $row) {
+                echo "<option value='".$row['id']."'>".get_display_name($row['id'])." (".$row['id'].") </option>";
+            }
+            ?>
+        </select>
+    </div>
+    <div class="container">
+        <label for="app">Token app</label>
+        <select name="app" id="app">
+            <option value="null">None</option>
+            <?php
+            $users = db_query("SELECT * FROM apps");
+            foreach ($users as $row) {
+                echo "<option value='".$row['id']."'>". $row['title'] ."</option>";
+            }
+            ?>
+        </select>
+    </div>
+
+    <input type="hidden" id="permissions" name="permissions" value="0" />
+
+    <h2>Permissions</h2>
+    <p>Permission number: <span id="permissionnumber"></span></p>
+    <div class="checkboxes container">
+        <input type="checkbox" id="account.email" value="1" /><label for="account.email"><code>account.email</code></label>
+        <input type="checkbox" id="account.settings" value="2" /><label for="account.settings"><code>account.settings</code></label>
+    </div>
+
+    <button type="submit">Generate!</button>
+</form>
+
+<style>
+    form .container {
+        display: unset;
+    }
+</style>
+
+<script>
+    const displayNumber = document.getElementById("permissionnumber");
+    const permissionsInput = document.getElementById("permissions");
+    const checkboxes = document.querySelectorAll("input[type='checkbox']");
+
+    console.log(checkboxes);
+
+    function updateCheckboxes() {
+        let permissions = 0;
+
+        for (let checkbox of checkboxes) {
+            if (checkbox.checked) {
+                permissions += Number(checkbox.value);
+            }
+        }
+
+        displayNumber.innerText = permissions.toString();
+        permissionsInput.value = permissions;
+    }
+
+    for (let checkbox of checkboxes) {
+        checkbox.onchange = updateCheckboxes;
+    }
+
+    updateCheckboxes();
+
+</script>
diff --git a/api_handler.php b/api_handler.php
index 2792cb1..164e5c0 100644
--- a/api_handler.php
+++ b/api_handler.php
@@ -7,8 +7,6 @@ if (array_key_exists('HTTP_AUTHORIZATION', $_SERVER)) {
     $access_token = str_replace("Bearer ", "", $_SERVER['HTTP_AUTHORIZATION']);
 }
 
-
-
 if (!empty($access_token)) {
     // Check who the access token belongs to
     $token = db_execute("SELECT * FROM tokens WHERE access_token = ?", [$access_token]);
@@ -27,7 +25,6 @@ function check_authorisation($token=""): int
     global $token_owner;
     // Validate token
     if (!validate_access_token($token) && "" != $token) {
-        echo "invalid";
         return 0; // Unauthorised
     }
 
@@ -50,8 +47,9 @@ function check_authorisation($token=""): int
     }
 
     return match ($token_row['type']) {
-        "dangerous" => 22,
-        "basic"     => 1,
+        "dangerous" => 1<<0 | 1<<1, // Everything
+        "basic"     => 1<<1, // Basic
+        "oauth"     => $token_row['permissions'],
         default     => 0,
     };
 }
@@ -93,16 +91,12 @@ function api_user_info(): array
     // `display_name`   = 1 (basic)
     // `id`             = 1 (basic)
     // `email`          = 1 (basic)
-
     $level = check_authorisation($access_token);
-
     $data = null;
-
-    if ($level >= 1) {
+    if ($level & (1 << 0)) {
         $data = db_execute("SELECT id, email, display_name FROM accounts WHERE id = ? LIMIT 1", [$token_owner]);
-    } if ($level == 22) {
-        $data = db_execute("SELECT * FROM accounts WHERE id = ? LIMIT 1", [$token_owner]);
-        unset($data['password']);
+    } else {
+        $data = db_execute("SELECT id, display_name FROM accounts WHERE id = ? LIMIT 1", [$token_owner]);
     }
 
     if (null != $data) {
@@ -117,7 +111,50 @@ function api_user_info(): array
         "response_code" => 401,
         "message" => "Unauthorized."
     ];
+}
+
+function api_settings(): array
+{
+    // GET: Return all settings
+    // POST/PATCH: Update settings
+
+    global $access_token, $token_owner;
+
+    $level = check_authorisation($access_token);
+
+    if (!($level & (1 << 1))) { // account.settings
+        http_response_code(401);
+        return [
+            "response_code" => 401,
+            "message" => "Unauthorized."
+        ];
+    }
+
+    if ($_SERVER['REQUEST_METHOD'] === "POST") {
+
+        // Now for the fucking worstest code ever
+        $settings_changed = json_decode(file_get_contents('php://input'), true);
+
+        if (isset($settings_changed['account'])) {
+            if (isset($settings_changed['account']['display_name'])) {
+                $display_name = db_execute('UPDATE accounts SET display_name = ? WHERE id = ?',
+                    [$settings_changed['account']['display_name'], $token_owner]);
+            }
+        }
+    }
+
+    // Get account settings
+    $display_name = db_execute('SELECT display_name FROM accounts WHERE id = ?', [$token_owner])["display_name"];
+
 
+    return [
+        "response_code" => 200,
+        "settings" => [
+            "account" => [
+                "display_name" => $display_name,
+            ]
+        ]
+    ];
 }
 
 $api_routes = [ // base url is base_url.'/api'
@@ -129,6 +166,9 @@ $api_routes = [ // base url is base_url.'/api'
     // Account stuff
     "/account/me" => "api_user_info",
 
+    // Settings
+    "/settings" => "api_settings",
+
     // Get avatar
     "/avatars/get" => "get_avatar"
 ];
@@ -142,6 +182,7 @@ if (isset($api_routes[$path])) {
             "response_code" => "498",
             "message" => "Token expired or invalid."
         ]));
+        exit();
     }
     $response = $api_routes[$path]();
     if (array_key_exists('response_code', $response)) {
diff --git a/assets/buttons/signinwith.svg b/assets/buttons/signinwith.svg
new file mode 100644
index 0000000..ca72560
--- /dev/null
+++ b/assets/buttons/signinwith.svg
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+        width="300.858"
+        height="50"
+        viewBox="0 0 79.602012 13.229167"
+        version="1.1"
+        id="svg1"
+        xml:space="preserve"
+        inkscape:export-filename="bitmap.png"
+        inkscape:export-xdpi="192"
+        inkscape:export-ydpi="192"
+        inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
+        sodipodi:docname="bcidsignin.svg"
+        xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+        xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+        xmlns="http://www.w3.org/2000/svg"
+        xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
+     id="namedview1"
+     pagecolor="#ffffff"
+     bordercolor="#000000"
+     borderopacity="0.25"
+     inkscape:showpageshadow="true"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="true"
+     inkscape:deskcolor="#d1d1d1"
+     inkscape:document-units="px"
+     showborder="false"
+     labelstyle="default"
+     inkscape:zoom="2.8649832"
+     inkscape:cx="165.446"
+     inkscape:cy="-36.300388"
+     inkscape:window-width="1920"
+     inkscape:window-height="1008"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="layer2" /><defs
+        id="defs1" /><g
+        inkscape:groupmode="layer"
+        id="layer2"
+        inkscape:label="Layer 2"><rect
+       style="display:inline;opacity:1;fill:#efdd8d;fill-opacity:1;stroke:#dcc455;stroke-width:0.261525;stroke-dasharray:none;stroke-opacity:1"
+       id="rect35"
+       width="79.340492"
+       height="12.967643"
+       x="0.13076229"
+       y="0.13076213"
+       ry="2.0748229"
+       rx="2.1097128"
+       inkscape:label="Background" /><g
+        style="shape-rendering:crispEdges"
+        id="g32"
+        transform="matrix(0.52916667,0,0,0.52916667,9.5250001,3.7041667)"
+        inkscape:label="BCID person"><g
+         id="layer1-5"
+         inkscape:label="BCID logo"><g
+           id="g27"
+           inkscape:label="Body"
+           style="opacity:1"><path
+             id="rect26"
+             style="opacity:1;fill:#000000;fill-opacity:1"
+             d="M 1.0058594,6.5 C 1.0026133,6.5 1,6.5026133 1,6.5058594 V 7.4941406 C 1,7.4973867 1.0026133,7.5 1.0058594,7.5 H 0.00585938 C 0.00261328,7.5 0,7.5026133 0,7.5058594 V 11.494141 C 0,11.497387 0.00261328,11.5 0.00585938,11.5 H 7.9941406 C 7.9973867,11.5 8,11.497387 8,11.494141 V 7.5058594 C 8,7.5026133 7.9973867,7.5 7.9941406,7.5 h -1 C 6.9973867,7.5 7,7.4973867 7,7.4941406 V 6.5058594 C 7,6.5026133 6.9973867,6.5 6.9941406,6.5 Z"
+             inkscape:label="Body" /></g><g
+        id="g31"
+        inkscape:label="Head"><path
+             id="rect28"
+             style="opacity:1;fill:#000000;fill-opacity:1"
+             d="M 1.0058594,-0.5 C 1.0026133,-0.5 1,-0.49738672 1,-0.49414062 V 0.49414062 C 1,0.49738672 1.0026133,0.5 1.0058594,0.5 H 1.9941406 C 1.9973867,0.5 2,0.49738672 2,0.49414062 V -0.49414062 C 2,-0.49738672 1.9973867,-0.5 1.9941406,-0.5 Z m 2,0 C 3.0026133,-0.5 3,-0.49738672 3,-0.49414062 V 0.49414062 C 3,0.49738672 3.0026133,0.5 3.0058594,0.5 H 4.9941406 C 4.9973867,0.5 5,0.49738672 5,0.49414062 V -0.49414062 C 5,-0.49738672 4.9973867,-0.5 4.9941406,-0.5 Z m -1,1 C 2.0026133,0.5 2,0.50261328 2,0.50585938 V 1.4941406 C 2,1.4973867 2.0026133,1.5 2.0058594,1.5 h -1 C 1.0026133,1.5 1,1.5026133 1,1.5058594 V 5.4941406 C 1,5.4973867 1.0026133,5.5 1.0058594,5.5 H 6.9941406 C 6.9973867,5.5 7,5.4973867 7,5.4941406 V 1.5058594 C 7,1.5026133 6.9973867,1.5 6.9941406,1.5 h -4 C 2.9973867,1.5 3,1.4973867 3,1.4941406 V 0.50585938 C 3,0.50261328 2.9973867,0.5 2.9941406,0.5 Z"
+             inkscape:label="Head" /></g></g></g><g
+        id="text32"
+        style="font-weight:500;font-size:4.58611px;font-family:Montserrat;-inkscape-font-specification:'Montserrat, Medium';letter-spacing:-0.079375px;word-spacing:0.0926042px;stroke-width:0;stroke-miterlimit:8.7"
+        inkscape:label="Sign in"
+        transform="scale(0.95030748,1.052291)"
+        aria-label="Sign in with ByeCorps ID"><path
+         style="font-weight:normal;-inkscape-font-specification:'Montserrat, Normal';opacity:1"
+         d="m 19.328294,7.9399255 c 0.820914,0 1.196975,-0.4035777 1.196975,-0.875947 0,-1.1694581 -1.976613,-0.6374693 -1.976613,-1.5180025 0,-0.3210277 0.261408,-0.582436 0.84843,-0.582436 0.284339,0 0.605367,0.087136 0.880533,0.2659944 L 20.392272,4.9589539 C 20.13545,4.7800956 19.759389,4.6746151 19.397086,4.6746151 c -0.816327,0 -1.183216,0.4081638 -1.183216,0.8805331 0,1.1878025 1.976613,0.6466416 1.976613,1.5271747 0,0.3164416 -0.261408,0.5686777 -0.862189,0.5686777 -0.421922,0 -0.830085,-0.1651 -1.059391,-0.3898194 l -0.132997,0.2614083 c 0.238478,0.2476499 0.710847,0.417336 1.192388,0.417336 z m 1.952271,-2.9672133 c 0.137584,0 0.238478,-0.1054805 0.238478,-0.2384777 0,-0.123825 -0.105481,-0.2247194 -0.238478,-0.2247194 -0.132997,0 -0.238477,0.1054806 -0.238477,0.2293055 0,0.1284111 0.10548,0.2338916 0.238477,0.2338916 z m 0.160514,2.9396966 V 5.5001149 H 21.115465 V 7.9124088 Z M 24.19133,5.963312 C 23.989541,5.6468704 23.640997,5.4817705 23.237419,5.4817705 c -0.687917,0 -1.210733,0.4723693 -1.210733,1.1648719 0,0.6925027 0.522816,1.1740442 1.210733,1.1740442 0.394405,0 0.738364,-0.1605138 0.940153,-0.4677832 v 0.3072694 c 0,0.5961943 -0.279753,0.875947 -0.889706,0.875947 -0.371475,0 -0.720019,-0.123825 -0.949325,-0.3301999 l -0.1651,0.2476499 c 0.252236,0.2384777 0.683331,0.3714749 1.123597,0.3714749 0.797984,0 1.206147,-0.376061 1.206147,-1.2061469 V 5.5001149 H 24.19133 Z m -0.921808,1.5684497 c -0.531989,0 -0.912636,-0.3623027 -0.912636,-0.8851193 0,-0.5228165 0.380647,-0.8805331 0.912636,-0.8805331 0.531989,0 0.917222,0.3577166 0.917222,0.8805331 0,0.5228166 -0.385233,0.8851193 -0.917222,0.8851193 z m 3.296006,-2.0499912 c -0.417336,0 -0.74295,0.169686 -0.917222,0.4631971 V 5.5001149 H 25.33645 v 2.4122939 h 0.325614 V 6.6466424 c 0,-0.5503332 0.325614,-0.8713609 0.843844,-0.8713609 0.458611,0 0.724606,0.2614083 0.724606,0.7704665 v 1.3666608 h 0.325614 V 6.5136452 c 0,-0.6925026 -0.403578,-1.0318747 -0.9906,-1.0318747 z m 3.17623,-0.5090583 c 0.137583,0 0.238478,-0.1054805 0.238478,-0.2384777 0,-0.123825 -0.105481,-0.2247194 -0.238478,-0.2247194 -0.132997,0 -0.238478,0.1054806 -0.238478,0.2293055 0,0.1284111 0.105481,0.2338916 0.238478,0.2338916 z m 0.160514,2.9396966 V 5.5001149 H 29.576658 V 7.9124088 Z M 31.96002,5.4817705 c -0.417336,0 -0.74295,0.169686 -0.917222,0.4631971 V 5.5001149 h -0.311856 v 2.4122939 h 0.325614 V 6.6466424 c 0,-0.5503332 0.325614,-0.8713609 0.843844,-0.8713609 0.458611,0 0.724606,0.2614083 0.724606,0.7704665 V 7.9124088 H 32.95062 V 6.5136452 c 0,-0.6925026 -0.403578,-1.0318747 -0.9906,-1.0318747 z M 37.447649,7.5501061 36.677182,5.5001149 H 36.397429 L 35.622377,7.5501061 34.865669,5.5001149 h -0.311856 l 0.90805,2.4122939 h 0.307269 l 0.761295,-1.9766134 0.761294,1.9766134 h 0.307269 l 0.912636,-2.4122939 h -0.298097 z m 1.640415,-2.5773939 c 0.137583,0 0.238477,-0.1054805 0.238477,-0.2384777 0,-0.123825 -0.10548,-0.2247194 -0.238477,-0.2247194 -0.132998,0 -0.238478,0.1054806 -0.238478,0.2293055 0,0.1284111 0.10548,0.2338916 0.238478,0.2338916 z m 0.160514,2.9396966 V 5.5001149 h -0.325614 v 2.4122939 z m 1.984371,-0.3806471 c -0.09172,0.08255 -0.224719,0.123825 -0.357717,0.123825 -0.27058,0 -0.417336,-0.1559278 -0.417336,-0.4402666 V 5.7752815 h 0.733778 V 5.5001149 H 40.457896 V 4.9727122 h -0.325614 v 0.5274027 h -0.431094 v 0.2751666 h 0.431094 v 1.458383 c 0,0.4448527 0.252237,0.7016749 0.706261,0.7016749 0.188031,0 0.380648,-0.055033 0.509059,-0.1696861 z m 1.855963,-2.0499912 c -0.408164,0 -0.724606,0.1605138 -0.903464,0.4402665 V 4.5095151 h -0.325614 v 3.4028937 h 0.325614 V 6.6466424 c 0,-0.5503332 0.325614,-0.8713609 0.843844,-0.8713609 0.458611,0 0.724606,0.2614083 0.724606,0.7704665 v 1.3666608 h 0.325614 V 6.5136452 c 0,-0.6925026 -0.403578,-1.0318747 -0.9906,-1.0318747 z"
+         id="path40" /><path
+        style="font-weight:bold;-inkscape-font-specification:'Montserrat, Bold'"
+        d="m 48.383925,6.2430647 c 0.261408,-0.1375833 0.426508,-0.3852332 0.426508,-0.7062609 0,-0.499886 -0.41275,-0.8346721 -1.215319,-0.8346721 h -1.56845 v 3.2102771 h 1.660172 c 0.843844,0 1.284111,-0.3210277 1.284111,-0.875947 0,-0.4035777 -0.229306,-0.6787443 -0.587022,-0.7933971 z M 47.503392,5.2616372 c 0.362302,0 0.559505,0.123825 0.559505,0.376061 0,0.2522361 -0.197203,0.3806472 -0.559505,0.3806472 H 46.765028 V 5.2616372 Z M 46.765028,7.3529034 V 6.5595063 h 0.866775 c 0.385233,0 0.591608,0.1284111 0.591608,0.3989916 0,0.2751666 -0.206375,0.3944055 -0.591608,0.3944055 z M 50.469192,7.1190118 49.776689,5.4450816 h -0.738364 l 1.068564,2.4856717 -0.0092,0.022931 c -0.09631,0.2201333 -0.206375,0.3072694 -0.403578,0.3072694 -0.142169,0 -0.293511,-0.059619 -0.403578,-0.1559277 l -0.261408,0.5090582 c 0.160514,0.1421694 0.43568,0.2247194 0.687917,0.2247194 0.444852,0 0.784224,-0.1788583 1.022702,-0.7750526 L 51.854197,5.4450816 H 51.16628 Z m 3.947232,-0.4310944 c 0,-0.793397 -0.559506,-1.2795247 -1.284111,-1.2795247 -0.752122,0 -1.316214,0.5319888 -1.316214,1.2703525 0,0.7337776 0.55492,1.2703525 1.407936,1.2703525 0.444853,0 0.788811,-0.1375833 1.018117,-0.3989916 L 53.861505,7.1373562 c -0.169686,0.1605139 -0.357717,0.2384777 -0.619125,0.2384777 -0.376061,0 -0.63747,-0.1880305 -0.706261,-0.4952998 h 1.866547 c 0.0046,-0.05962 0.01376,-0.1375834 0.01376,-0.1926167 z M 53.136899,5.9495537 c 0.321028,0 0.55492,0.2017888 0.605367,0.5136443 h -1.215319 c 0.05045,-0.3164416 0.284338,-0.5136443 0.609952,-0.5136443 z"
+        id="path41" /><path
+        style="font-weight:600;-inkscape-font-specification:'Montserrat, Semi-Bold'"
+        d="m 56.400799,7.9582699 c 0.527402,0 0.976841,-0.1880305 1.274938,-0.5365749 L 57.290504,7.0548062 c -0.233892,0.2568222 -0.522816,0.3806472 -0.857602,0.3806472 -0.664986,0 -1.146528,-0.4677833 -1.146528,-1.1281831 0,-0.6603999 0.481542,-1.1281831 1.146528,-1.1281831 0.334786,0 0.62371,0.123825 0.857602,0.376061 L 57.675737,5.1928455 C 57.37764,4.8443012 56.928201,4.6562706 56.405385,4.6562706 c -0.986014,0 -1.719791,0.6925027 -1.719791,1.6509997 0,0.958497 0.733777,1.6509996 1.715205,1.6509996 z m 2.722731,-0.013758 c 0.761295,0 1.307042,-0.5228166 1.307042,-1.2565942 0,-0.7337776 -0.545747,-1.252008 -1.307042,-1.252008 -0.752122,0 -1.302455,0.5182304 -1.302455,1.252008 0,0.7337776 0.550333,1.2565942 1.302455,1.2565942 z m 0,-0.4907138 c -0.41275,0 -0.724605,-0.2980971 -0.724605,-0.7658804 0,-0.4677832 0.311855,-0.7658804 0.724605,-0.7658804 0.417336,0 0.729192,0.2980972 0.729192,0.7658804 0,0.4677833 -0.311856,0.7658804 -0.729192,0.7658804 z M 61.447281,5.463426 h -0.545747 v 2.4489828 h 0.573263 V 6.7246063 c 0,-0.499886 0.275167,-0.7567082 0.710848,-0.7567082 0.04127,0 0.08255,0.00459 0.132997,0.013758 V 5.4359094 c -0.408164,0 -0.706261,0.128411 -0.871361,0.3852332 z m 2.635605,-0.027517 c -0.334786,0 -0.619125,0.1146527 -0.811741,0.3485443 V 5.463426 h -0.545747 v 3.3386882 h 0.573263 V 7.6143117 c 0.197203,0.2247194 0.47237,0.3301999 0.784225,0.3301999 0.715433,0 1.242836,-0.4952999 1.242836,-1.2565942 0,-0.7567081 -0.527403,-1.252008 -1.242836,-1.252008 z m -0.06421,2.0178884 c -0.41275,0 -0.729192,-0.2980971 -0.729192,-0.7658804 0,-0.4677832 0.316442,-0.7658804 0.729192,-0.7658804 0.41275,0 0.724605,0.2980972 0.724605,0.7658804 0,0.4677833 -0.311855,0.7658804 -0.724605,0.7658804 z m 2.530125,0.4907138 c 0.687916,0 1.109838,-0.2980972 1.109838,-0.7567082 0,-0.958497 -1.513416,-0.5182304 -1.513416,-1.0135303 0,-0.1605139 0.1651,-0.2751666 0.513644,-0.2751666 0.233892,0 0.467784,0.045861 0.701675,0.1834444 l 0.220134,-0.4356805 c -0.220134,-0.1329972 -0.591609,-0.210961 -0.917222,-0.210961 -0.6604,0 -1.077736,0.3026832 -1.077736,0.7658804 0,0.9768414 1.513416,0.5365748 1.513416,1.0043581 0,0.169686 -0.151342,0.2705805 -0.513644,0.2705805 -0.30727,0 -0.63747,-0.1008945 -0.853017,-0.2430639 L 65.512345,7.669345 c 0.220133,0.1559277 0.628297,0.2751666 1.036461,0.2751666 z"
+        id="path42" /><path
+        style="font-weight:normal;-inkscape-font-specification:'Montserrat, Normal';opacity:1"
+        d="M 69.775483,7.9124088 V 4.7021317 H 69.43611 v 3.2102771 z m 2.273299,0 c 1.027288,0 1.719791,-0.6603998 1.719791,-1.6051385 0,-0.9447387 -0.692503,-1.6051386 -1.719791,-1.6051386 H 70.74174 v 3.2102771 z m -0.96767,-2.916766 h 0.949325 c 0.853017,0 1.40335,0.541161 1.40335,1.3116275 0,0.7704665 -0.550333,1.3116275 -1.40335,1.3116275 h -0.949325 z"
+        id="path43" /></g></g></svg>
diff --git a/head.php b/head.php
index 1998630..5ea88ad 100644
--- a/head.php
+++ b/head.php
@@ -7,7 +7,7 @@
 ></script>
 <title><?php if (isset($doc_title)) { echo $doc_title." | "; } ?>ByeCorps ID</title>
 
-<link rel="shortcut icon" href="favicon.svg" type="image/svg" />
+<link rel="shortcut icon" href="/favicon.svg" type="image/svg" />
 
 <link rel="manifest" href="manifest.json" />
 
diff --git a/index.php b/index.php
index 9f068c5..3d7a309 100755
--- a/index.php
+++ b/index.php
@@ -120,12 +120,14 @@ $paths = array(
     "/admin/list/accounts" => ["admin_accounts.php"],
     "/admin/list/apps" => ["admin_apps.php"],
     "/admin/create/app" => ["admin_apps_create.php"],
+    "/admin/create/token" => ["admin_create_token.php"],
     "/admin/signinas" => ["signinas.php"],
     "/admin/purge" => ["admin_purge.php"],
 
     // Settings
     "/dashboard" => ["dashboard.php", "Dashboard"],
     "/settings" => ["settings.php", "Settings"],
+    "/settings/account" => ["settings_account.php", "Settings -> Account"],
 
     "/account" => ["account.php", "Your account"],
     "/signin" => ["signin.php", "Sign in"],
diff --git a/landing.php b/landing.php
index a4ea89b..c4769b4 100644
--- a/landing.php
+++ b/landing.php
@@ -1,3 +1,11 @@
+<?php
+
+if (isset($_GET['new_landing'])) {
+    goto new_landing;
+}
+
+?>
+
 <div class="hero">
     <div class="hero-text">
         <img src="/assets/bcid.svg" alt="ByeCorps ID Logo" class="logo">
@@ -5,10 +13,26 @@
         <p>Log into ByeCorps and beyond with a single ID.</p>
         <!-- <p><input type="email" name="loginEmail" id="loginEmail" placeholder="Email" /></p> -->
 
-        <?php 
-        if ( $_SESSION['auth']) { echo "<a href='/account' class='button primary'>Manage account</a>"; } 
-        else { echo "<a href='/signin' class='button primary'>Sign in</a><a href='/signup' class='button'>Create an account</a>"; } 
+        <?php
+        if ( $_SESSION['auth']) { echo "<a href='/account' class='button primary'>Manage account</a>"; }
+        else { echo "<a href='/signin' class='button primary'>Sign in</a><a href='/signup' class='button'>Create an account</a>"; }
         ?>
 
     </div>
-</div>
\ No newline at end of file
+</div>
+
+<?php
+
+exit;
+
+new_landing:
+
+?>
+
+<div class="hero">
+    <div class="hero-text">
+        <img src="/assets/bcid.svg" alt="ByeCorps ID Logo" class="logo">
+        <h1><span class="bc-1">Bye</span><span class="bc-2">Corps</span><span class="bc-3"> ID</span></h1>
+        <p>Log into ByeCorps and beyond with a single ID.</p>
+</div>
+
diff --git a/login_external_basic.php b/login_external_basic.php
index af0e93e..2a1c68c 100644
--- a/login_external_basic.php
+++ b/login_external_basic.php
@@ -31,9 +31,29 @@ if ($query['callback'] != $app['callback']) {
     goto login;
 }
 
+if ($_SESSION['auth'] && $_SERVER['REQUEST_METHOD'] == 'GET') {
+    // We can check if there's already a valid token of the same level and just pass that on instead.
+    $valid_tokens = db_execute_all("SELECT * FROM tokens WHERE owner_id = ? AND type = ? AND application_id = ? AND expiry > ?",
+        [$_SESSION['id'], "basic", $app_id, time()]);
+
+    if (sizeof($valid_tokens) > 0) {
+        print_r($valid_tokens);
+
+        $token = $valid_tokens[0];
+
+        header('Location: '. $_GET['callback'].'?access_token='.$token['access_token'].'&refresh='.$token['refresh_token']
+                .'&expiry='.$token['expiry']);
+        exit();
+    }
+
+//    if (validate_access_token())
+
+
+}
+
 if ($_SERVER['REQUEST_METHOD'] === 'POST') {
     // Here's a few easy steps to figure out if we should give the other party a token or not.
-    print_r($_POST);
+//    print_r($_POST);
 
     // First: match the session ids. If they aren't the same it's probably Not Ok.
     if (session_id() != $_POST['sessionid']) {
@@ -74,7 +94,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
     }
 
     // The following gets run assuming we know the client is the one CLICKING the button.
-    $tokens = generate_basic_access_token($_POST['bcid']);
+    $tokens = generate_basic_access_token($_POST['bcid'], $app_id);
 
     header('Location: '. $_POST['callback'].'?access_token='.$tokens['access'].'&refresh='.$tokens['refresh']
                                 .'&expiry='.$tokens['expiry']);
@@ -94,15 +114,15 @@ login:
                 <img src="<?= $app['icon'] ?>" alt="<?= htmlspecialchars($user['title']) ?>'s avatar" />
             </div>
             <h1>Sign into <?= htmlspecialchars($app['title']) ?></h1>
-            <p class="subtitle">Owned by <strong><?= get_display_name($app['owner_id'], put_bcid_in_parenthesis: true) ?></strong></p>
+            <p class="subtitle">Owned by <strong><?= htmlspecialchars( get_display_name($app['owner_id'], put_bcid_in_parenthesis: true) ) ?></strong></p>
 <!--            <p>--><?php //= htmlspecialchars($app['description']) ?><!--</p>-->
             <?php
             error_no_app:
             if ($error) {
                 http_response_code($error[1]);
                 echo "
-<div class='error center'>
-<span class='fa-regular fa-2xl center fa-xmark-circle'></span>
+<div class='error center vertical-center'>
+<span class='fg-error fa-regular fa-2xl center fa-xmark-circle'></span>
 <h2>Something went wrong!</h2>
 <p>Server returned error:<br /><code>$error[0]</code> (HTTP response code $error[1])</p>
 </div>
@@ -110,9 +130,9 @@ login:
                 goto dont_show_form;
             }
             ?>
-            <p><strong><?= $app['title'] ?></strong> uses ByeCorps ID for authentication.</p>
+            <p><strong><?= htmlspecialchars($app['title']) ?></strong> uses ByeCorps ID for authentication.</p>
             <p>Please double-check the information and avoid signing in with your BCID if you do not trust this app.</p>
-            <p>Please confirm that you'd like to sign into <strong><?= $app['title'] ?></strong>.</p>
+            <p>Please confirm that you'd like to sign into <strong><?= htmlspecialchars($app['title']) ?></strong>.</p>
             <?php
             if (null != $flash) {
                 echo "<p class='flash'>$flash</p>";
@@ -131,11 +151,10 @@ login:
                 <input type="email" autocomplete="email" name="email" id="email" placeholder="Email" />
                 <input type="password" name="password" id="password" placeholder="Password" />
                 <?php signedin: ?>
-                <button class="primary" type="submit">Sign into <?= $app['title']; ?></button>
-                <button class="secondary" type="reset">GET ME OUT OF HERE</button>
+                <button class="primary" type="submit">Sign into <?= htmlspecialchars($app['title']) ?></button>
                 <p class="subtitle center">
-                    You will be brought to <strong><?= $query['callback'] ?></strong>.
-                    <br /><?= $app['title'] ?> will be able to see your email and display name.
+                    You will be brought to <strong><?= htmlspecialchars($query['callback']) ?></strong>.
+                    <br /><strong><?= htmlspecialchars($app['title']) ?></strong> will be able to see your email and display name.
                 </p>
                 <input type="hidden" name="callback" value="<?= $query['callback'] ?>" />
             </form>
diff --git a/profile.php b/profile.php
index 4f65db5..1636266 100644
--- a/profile.php
+++ b/profile.php
@@ -33,7 +33,7 @@ if ($_SESSION['id'] != $profile['id']) {
 }
 
 // Get badges owned by this person
-$badges = db_execute_all('SELECT * FROM badge_owners INNER JOIN badges b on badge_owners.badge_id = b.id; ', []);
+$badges = db_execute_all('SELECT * FROM badge_owners INNER JOIN badges b on badge_owners.badge_id = b.id WHERE owner_id = ?; ', [$profile['id']]);
 if (!empty($badges)) {
     if (!array_is_list($badges)) {
         $badges = array (0 => $badges);
@@ -42,49 +42,3 @@ if (!empty($badges)) {
 
 ?>
 
-<div id="profile">
-    <img src="<?= $avatar ?>" class="avatar" alt="Avatar">
-    <div class="info">
-        <div class="displayname"><?= htmlspecialchars($display_name) ?></div>
-        <div class="bcid"><?= format_bcid( $profile['id'] ); ?></div>
-    </div>
-</div>
-
-<div id="details">
-    <div id="badges">
-        <h2>Badges</h2>
-        <?php
-        if (empty($badges)) {
-            echo '<p>This profile has no badges :(</p>';
-        } else {
-            foreach ($badges as $badge) {
-                echo "<div class='badge'>
-<img src='". $badge['image'] ."' alt='". htmlspecialchars($badge['title']) ."' />
-<div class='details'>
-<span class='title'>" . htmlspecialchars($badge['title']) . "</span>
-<p>". htmlspecialchars($badge['description']) ."</p>
-<p class='subtitle'>". htmlspecialchars($badge['description']) ."</p>
-<p class='earned subtitle'>Earned " . $badge['earned'] . "</p>
-</div>
-</div>";
-            }
-        }
-        ?>
-    </div>
-
-    <div id="info">
-        <h2>Info</h2>
-
-        <table>
-            <tr>
-                <th>Joined</th>
-                <td><?= $user['created_date'] ?></td>
-            </tr>
-            <tr>
-                <th>Badges earned</th>
-                <td><?= count($badges) ?></td>
-            </tr>
-        </table>
-
-    </div>
-</div>
diff --git a/settings_account.php b/settings_account.php
new file mode 100644
index 0000000..08cf72f
--- /dev/null
+++ b/settings_account.php
@@ -0,0 +1,79 @@
+<?php
+
+if (empty($_SESSION)) {
+    http_response_code(307);
+    header('Location: /signin?callback=/dashboard');
+    exit();
+}
+if (!$_SESSION['auth']) {
+    http_response_code(307);
+    header('Location: /signin?callback=/dashboard');
+    exit;
+}
+
+?>
+
+<link href="/styles/settings.css" rel="stylesheet" />
+
+<div id="settings_split">
+    <div id="mini_profile" class="left">
+        <div class="image_container" data-backgroundcolour="white">
+            <img src="<?= get_avatar_url($user['id']) ?>"  alt="<?= htmlspecialchars(get_display_name($user['id'])) ?>'s avatar"/>
+        </div>
+        <div class="texts">
+            <span class="displayname"><?= htmlspecialchars(get_display_name($user['id'], false)) ?></span>
+            <span class="id bcid"><?= format_bcid($user['id']) ?></span>
+        </div>
+    </div>
+
+    <div class="other">
+        <h1>Account</h1>
+        <form method="post">
+            <div class="container">
+                <label for="display_name"><span id="display_name_label"></span> Display name</label>
+                <input type="text" name="display_name" id="display_name" data-field="account.display_name" placeholder="<?= htmlspecialchars(format_bcid($user['id'])) ?>" value="<?= htmlspecialchars($user['display_name']) ?>">
+            </div>
+
+            <button type="submit">Save</button>
+        </form>
+    </div>
+</div>
+
+<style>
+    /*form label {*/
+    /*    padding-right: 1rem;*/
+    /*}*/
+</style>
+
+<script>
+    const display_name_box = document.getElementById("display_name");
+
+    async function updateJsonSettings(data) {
+        const response = await fetch("https://id.byecorps.com/api/settings", {
+            method: "POST", // or 'PUT'
+            headers: {
+                "Content-Type": "application/json",
+            },
+            body: JSON.stringify(data),
+        });
+
+        const result = await response.json();
+        console.log("Success:", result);
+        return result;
+    }
+
+    display_name_box.onchange = _ => {
+        document.getElementById("display_name_label").classList = "fa-solid fa-spinner fa-spin-pulse";
+
+        updateJsonSettings({
+            account: {
+                display_name: display_name_box.value,
+            }
+        }).then(_=>{
+            document.getElementById("display_name_label").classList = "fa-solid fa-check";
+        }).catch(_=>{
+            document.getElementById("display_name_label").classList = "fa-solid fa-triangle-exclamation";
+        });
+
+    }
+</script>
diff --git a/signin.php b/signin.php
index 7814a1a..0574d99 100644
--- a/signin.php
+++ b/signin.php
@@ -33,7 +33,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
             if ($_POST['keep_logged_in'] == "on") {
                 $token = generate_cookie_access_token($user['id']);
 //            print_r($token);
-                setcookie("keep_me_logged_in", $token['access']);
+                setcookie("keep_me_logged_in", $token['access'], time()+606024*365);
             }
         }
 
diff --git a/styles/colours.css b/styles/colours.css
index fe71ae5..b346a2f 100644
--- a/styles/colours.css
+++ b/styles/colours.css
@@ -24,6 +24,8 @@
     --grey-8: #343a40;
     --grey-9: #212529;
 
+    --error-color: var(--red-5);
+
     --background: white;
     --background-dark: #121212;
 
@@ -111,6 +113,10 @@ input[data-com-onepassword-filled="dark"] {
     color: black;
 }
 
+.fg-error {
+    color: var(--error-color);
+}
+
 @media screen and (prefers-color-scheme: dark) {
     :root {
         --background: #121212;
diff --git a/styles/profiles.css b/styles/profiles.css
index fb02a44..d5b4f78 100644
--- a/styles/profiles.css
+++ b/styles/profiles.css
@@ -1,4 +1,12 @@
 
+#profile {
+    display: flex;
+}
+
+#profile .avatar {
+    height: 150px;
+}
+
 #mini_profile {
     display: flex;
     gap: 1rem;
diff --git a/styles/types.css b/styles/types.css
index 26b27af..2861f85 100644
--- a/styles/types.css
+++ b/styles/types.css
@@ -52,6 +52,10 @@ h2.subheading + h1 {
     text-align: center;
 }
 
+.vertical-center {
+    vertical-align: center;
+}
+
 .icon-true::before {
     content: "\f00c";
 }
-- 
GitLab