diff --git a/VERSION b/VERSION
index a22da3b..a5ee8d9 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2024.12.06
+2024.12.23
diff --git a/_wip_todo/add_url.php b/_wip_todo/add_url.php
index 34494d1..886664c 100644
--- a/_wip_todo/add_url.php
+++ b/_wip_todo/add_url.php
@@ -10,9 +10,9 @@ if ($config['debug_mode']) {
require_once('session.php');
-if (!isset($_SESSION['valid_session'])) {
+if (!isset($_SESSION['valid_session'])) {
$secretKey = $config['totp_secret'];
- $cookieVal = decodeCookie($secretKey);
+ $cookieVal = isSavedCookieValid($secretKey);
if ($cookieVal === false) { # Valid cookie ?
header('Location: login.php');
@@ -56,27 +56,30 @@ if (isset($_POST['submit'])) {
exit;
}
} else { ?>
-
-
-
-
- twtxt
-
-
-
-
-
-
-
-
+
+
+
+
+
+ twtxt
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/_wip_todo/session.php b/_wip_todo/session.php
deleted file mode 100644
index 907fb00..0000000
--- a/_wip_todo/session.php
+++ /dev/null
@@ -1,101 +0,0 @@
- 'twtxt_session',
- 'use_strict_mode' => true,
- 'cookie_httponly' => true,
- 'cookie_secure' => $config['secure_cookies'],
- 'sid_length' => 64,
- 'sid_bits_per_character' => 6,
- 'cookie_samesite' => 'Strict', // Not compatible with PHP lower than 7.3
-]);
-
-function has_valid_session() {
- $config = parse_ini_file('private/config.ini');
- $secretKey = $config['password'];
-
- if (isset($_SESSION['valid_session'])) {
- return true;
- }
-
- $cookieVal = decodeCookie($secretKey);
- if ($cookieVal === false) {
- #echo "Invalid cookie";
- return false;
- }
-
- return true;
-}
-
-function encrypt(string $data, string $key, string $method): string {
- $ivSize = openssl_cipher_iv_length($method);
- $iv = openssl_random_pseudo_bytes($ivSize);
- $encrypted = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
- # PHP 8.2 - Deprecated: implode():
- # Passing null to parameter #1 ($separator) of type array|string is deprecated
- //$encrypted = strtoupper(implode(null, unpack('H*', $encrypted)));
- $encrypted = strtoupper(implode(unpack('H*', $encrypted)));
-
- return $encrypted;
-}
-
-function decrypt(string $data, string $key, string $method): string {
- $data = pack('H*', $data);
- $ivSize = openssl_cipher_iv_length($method);
- $iv = openssl_random_pseudo_bytes($ivSize);
- $decrypted = openssl_decrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
-
- return trim($decrypted);
-}
-
-function saveLoginSuccess($secretKey) {
- // Set a cookie to remember the user
- $_SESSION['valid_session'] = true;
-
- // Set a cookie value to remember the user
- $encoded_cookie_value = generateCookieValue('admin', $secretKey);
- $cookie_expiry = time() + (30 * 24 * 60 * 60); // 30 days
-
- $config = parse_ini_file('private/config.ini');
-
- setcookie(COOKIE_NAME, $encoded_cookie_value, [
- 'expires' => $cookie_expiry,
- 'secure' => $config['secure_cookies'],
- 'httponly' => true,
- 'samesite' => 'Strict',
- ]);
-}
-
-function generateCookieValue($username, $secretKey) {
- $key = bin2hex($secretKey);
-
- $encrypted = encrypt($username, $key, ENCRYPTION_METHOD);
- return $encrypted;
-}
-
-function decodeCookie($secretKey) {
- // Retrieve the encoded cookie name
- if (!isset($_COOKIE[COOKIE_NAME])) {
- return false;
- }
-
- $encoded_cookie_value = $_COOKIE[COOKIE_NAME];
- $key = bin2hex($secretKey);
-
- $config = parse_ini_file('private/config.ini');
-
- // Extend expiry by 30 days
- $cookie_expiry = time() + (30 * 24 * 60 * 60);
- setcookie(COOKIE_NAME, $encoded_cookie_value, [
- 'expires' => $cookie_expiry,
- 'secure' => $config['secure_cookies'],
- 'httponly' => true,
- 'samesite' => 'Strict',
- ]);
-
- $decrypted = decrypt($encoded_cookie_value, $key, ENCRYPTION_METHOD);
- return $decrypted;
-}
\ No newline at end of file
diff --git a/index.php b/index.php
index b4cb114..1e51db4 100644
--- a/index.php
+++ b/index.php
@@ -1,4 +1,4 @@
- 'new_twt.php',
'/add' => 'add_feed.php',
'/following' => 'following.php',
- //'/refresh' => 'load_twt_files.php',
'/refresh' => 'refresh.php',
'/login' => 'login.php',
'/logout' => 'logout.php',
'/profile' => 'profile.php',
'/replies' => 'replies.php',
'/gallery' => 'gallery.php',
- //'/profile/([a-zA-Z0-9_-]+)' => 'profile.php',
- '/conv/([a-zA-Z0-9]{7})' => 'conv.php', // matches only twtHash of exactly 7 alphanumeric characters
- '/post/([a-zA-Z0-9]{7})' => 'post.php', // matches only twtHash of exactly 7 alphanumeric characters
- //'/thumb' => 'thumb.php',
+ '/conv/([a-zA-Z0-9]{7})' => 'conv.php', // matches only twtHash of exactly 7 alphanumeric characters
+ '/post/([a-zA-Z0-9]{7})' => 'post.php', // matches only twtHash of exactly 7 alphanumeric characters
'/upload' => 'upload_img.php',
'/webmention' => 'webmention_endpoint.php',
+ //'/thumb' => 'thumb.php',
+ //'/profile/([a-zA-Z0-9_-]+)' => 'profile.php',
+
+ # Debug endpoints
+ '/test_login' => 'test_login.php',
];
// Loop through the defined routes and try to match the request URI
foreach ($routes as $pattern => $action) {
if (preg_match('#^' . $pattern . '$#', $path, $matches)) {
-
- // Extract any matched parameters (e.g., username)
+
+ // Extract any matched parameters (e.g., username)
if(!empty($matches[1])) {
//array_shift($matches);
$id = $matches[1];
diff --git a/libs/persistent_session.php b/libs/persistent_session.php
new file mode 100644
index 0000000..45d45fd
--- /dev/null
+++ b/libs/persistent_session.php
@@ -0,0 +1,115 @@
+ !isset($config[$key]));
+
+if (!empty($missing_keys)) {
+ die('Missing required keys in config.ini: ' . implode(', ', $missing_keys));
+}
+
+# To make it more secure, something like JWT could be used instead
+
+const COOKIE_NAME = 'timeline_login';
+const ENCRYPTION_METHOD = 'aes-256-cbc';
+const EXPIRATION_DAYS = 30;
+
+session_start([
+ 'name' => 'timeline_session',
+ 'use_strict_mode' => true,
+ 'cookie_httponly' => true,
+ 'cookie_secure' => $config['secure_cookies'],
+ 'sid_length' => 64,
+ 'sid_bits_per_character' => 6,
+ 'cookie_samesite' => 'Strict', # Not compatible with PHP < 7.3
+]);
+
+function hasValidSession(): bool|string {
+ # If short lived session is valid
+ if (isset($_SESSION['valid_session'])) {
+ return true;
+ }
+
+ # Otherwise, check the persistent cookie
+ return isSavedCookieValid();
+}
+
+function encrypt(string $data, string $key, string $method): string {
+ $ivSize = openssl_cipher_iv_length($method);
+ $iv = openssl_random_pseudo_bytes($ivSize);
+ $encrypted = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
+ $encrypted = strtoupper(implode(unpack('H*', $encrypted)));
+
+ return $encrypted;
+}
+
+function decrypt(string $data, string $key, string $method): string | bool {
+ $data = pack('H*', $data);
+ $ivSize = openssl_cipher_iv_length($method);
+ $iv = openssl_random_pseudo_bytes($ivSize);
+ $decrypted = openssl_decrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
+
+ var_dump($decrypted);
+
+ if ($decrypted === false) {
+ return false;
+ }
+
+ return trim($decrypted);
+}
+
+function saveLoginSuccess() {
+ $_SESSION['valid_session'] = true;
+
+ $config = parse_ini_file('private/config.ini');
+
+ # Set a cookie to remember the user
+ $cookieExpiry = EXPIRATION_DAYS * 24 * 60 * 60 + time();
+ $encodedCookieValue = generateCookieValue(strval($cookieExpiry), $config['secret_key']);
+
+ setcookie(COOKIE_NAME, $encodedCookieValue, [
+ 'expires' => $cookieExpiry,
+ 'secure' => $config['secure_cookies'],
+ 'httponly' => true,
+ 'samesite' => 'Strict',
+ ]);
+}
+
+function generateCookieValue($value, $secretKey) {
+ $key = bin2hex($secretKey);
+
+ $encrypted = encrypt($value, $key, ENCRYPTION_METHOD);
+ return $encrypted;
+}
+
+function isSavedCookieValid() {
+ if (!isset($_COOKIE[COOKIE_NAME])) {
+ return false;
+ }
+
+ $config = parse_ini_file('private/config.ini');
+
+ $encoded_cookie_value = $_COOKIE[COOKIE_NAME];
+ $key = bin2hex($config['secret_key']);
+
+ $cookieVal = decrypt($encoded_cookie_value, $key, ENCRYPTION_METHOD);
+
+ if ($cookieVal === false) {
+ deletePersistentCookie();
+ return false;
+ }
+
+ # TODO: Check that the cookie is not expired
+
+ saveLoginSuccess(); # Extend expiracy for previous cookie
+
+ return true; # If it was decoded correctly, it's a valid session
+}
+
+function deletePersistentCookie() {
+ if (isset($_COOKIE[COOKIE_NAME])) {
+ unset($_COOKIE[COOKIE_NAME]);
+ setcookie(COOKIE_NAME, '', time() - 3600);
+ }
+}
diff --git a/libs/session.php b/libs/session.php
index bb9f9fc..10a688b 100644
--- a/libs/session.php
+++ b/libs/session.php
@@ -1,30 +1,33 @@
\ No newline at end of file
diff --git a/partials/lists.php b/partials/lists.php
index 32dca1b..ac0d3a1 100644
--- a/partials/lists.php
+++ b/partials/lists.php
@@ -1,43 +1,43 @@
\ No newline at end of file
diff --git a/private/config_template.ini b/private/config_template.ini
index a49498c..cfef942 100644
--- a/private/config_template.ini
+++ b/private/config_template.ini
@@ -42,13 +42,18 @@ webmentions_txt_path = "./mentions.txt"
public_webmentions = "https://example.com/timeline/mentions.txt"
[security]
-; Generate it with the TOTP module
+; Secret key to encrypt cookies
+; Create a new one here: https://randomkeygen.com
+secret_key = "553GkZzIYZKx5z0lftt4yKDG4aKb4sAG"
+
+; Simple password
+password = "change_me"
+
+; A dynamic password (TOTP) changing every 30 seconds
+; Use a TOTP client with support for 10 digits like Aegis (Android)
totp_digits = 10
totp_secret = "1234567890"
; It's recommended that your site is hosted on HTTPS
; In case it's in HTTP (not secure), set this to false
secure_cookies = true
-
-; Simple password for unnamed user
-password = ""
\ No newline at end of file
diff --git a/views/following.php b/views/following.php
index a01c2ad..714e8cd 100644
--- a/views/following.php
+++ b/views/following.php
@@ -1,7 +1,7 @@
-->
Nick
URL
-
+
Time ago
-
-
- = $currentFollower[0] ?>
-
- = $currentFollower[1] ?>
-
-
-
-
-
-
- twts)) {
+
+
+ = $currentFollower[0] ?>
+
+ = $currentFollower[1] ?>
+
+
+
+
+
+
+ twts)) {
- // Then test if latest twt is at start or end of file:
- $resetVar = reset(getTwtsFromTwtxtString($currentFollower[1])->twts);
- $endVar = end(getTwtsFromTwtxtString($currentFollower[1])->twts);
- if ($resetVar->timestamp < $endVar->timestamp) { // TODO: this can be swapped to get time of first twt
- echo $endVar->displayDate;
- } else {
- echo $resetVar->displayDate;
+ // Then test if latest twt is at start or end of file:
+ $resetVar = reset(getTwtsFromTwtxtString($currentFollower[1])->twts);
+ $endVar = end(getTwtsFromTwtxtString($currentFollower[1])->twts);
+ if ($resetVar->timestamp < $endVar->timestamp) { // TODO: this can be swapped to get time of first twt
+ echo $endVar->displayDate;
+ } else {
+ echo $resetVar->displayDate;
+ }
}
- }
- ?>
+ ?>
-
-
+
+
-
+
-
+
\ No newline at end of file
diff --git a/views/login.php b/views/login.php
index dfa0d93..116824d 100644
--- a/views/login.php
+++ b/views/login.php
@@ -1,28 +1,30 @@
-
- Enter password or TOTP
-
-
+
+
+ Enter password or TOTP
+
+
-
+
\ No newline at end of file
diff --git a/views/logout.php b/views/logout.php
index c44d92b..3f1c868 100644
--- a/views/logout.php
+++ b/views/logout.php
@@ -1,6 +1,7 @@