Revamped the login system. Now you can be logged in on more devices, for longer periods of time. Todo: create script to clean user_login-table.

master
Eirik Th S 2022-01-19 23:32:26 +01:00
parent cc94a18c18
commit 02bd4d4131
5 changed files with 135 additions and 61 deletions

View File

@ -39,7 +39,8 @@ if(isset($_POST) && !empty($_POST)){
}
}
$err = loginUser($data['loginEmail'], $data['loginPwd']);
$stayLoggedIn = isset($_POST['stayLoggedIn']);
$err = loginUser($data['loginEmail'], $data['loginPwd'], $stayLoggedIn);
if($err === true){
header("Location: ".$returnToPage);
die();
@ -94,6 +95,12 @@ if(isset($_POST) && !empty($_POST)){
I agree to save a temporary cookie in my browser for the logged in functions to work.
</label>
</div>
<div class="form-check" style="margin: 3px 0;">
<input class="form-check-input" type="checkbox" id="rememberCheck" name="stayLoggedIn">
<label class="form-check-label" for="rememberCheck">
Remember me (for 30 days)
</label>
</div>
<button type="submit" class="btn btn-primary mb-3 w-100">Login</button>
</form>
<p>Do you not have an account? <a href="register.php">Register</a> </p>

View File

@ -1,13 +1,7 @@
<?php
require 'webdata/init.php';
if(checkLogin()){
logoutUser();
echo "logged out?";
}
else {
echo "not logged in";
}
logoutUser();
header("Location: ");
header("Location: ".getConfig('projectRoot')."/");

View File

@ -36,7 +36,7 @@ class Store {
// html += " <input type='text' class='form-control newItemName' placeholder='New Item Name' data-toggle='tooltip' title='New Item Name' aria-label='New Item Name'>";
// html += " <input type='number' class='form-control newItemPrice' value='0' min='0' step='.01' data-toggle='tooltip' title='Price' aria-label='Price'>";
html += " <div class='form-control form-floating'>";
html += " <input type='text' id='newItemName0' class='form-control newItemName' placeholder='New Item Name' aria-label='New Item Name'>";
html += " <input type='text' id='newItemName0' class='form-control newItemName' placeholder='New Item Name' aria-label='New Item Name' autocomplete='off' autocapitalize='on'>";
html += " <label for='newItemName0'>New Item Name</label>";
html += " </div>";
html += " <div class='form-control form-floating'>";

View File

@ -69,7 +69,7 @@ function getHtmlHeaders($prepend = ""){
\n";
}
function loginUser($email, $pass) {
function loginUser($email, $pass, $stayLoggedIn = false) {
global $db;
@ -89,23 +89,27 @@ function loginUser($email, $pass) {
}
if(password_verify(PwdGen($pass), $dbPass)){
if(!isset($_SESSION)){
session_start();
}
else {
// session_regenerate_id(true);
session_regenerate_id();
}
session_regenerate_id();
$_SESSION['user_id'] = $dbUserId;
$_SESSION['user_name'] = $dbUserName;
$_SESSION['user_agent'] = md5($_SERVER['HTTP_USER_AGENT']);
$md5agent = md5($_SERVER['HTTP_USER_AGENT']);
$userKey = GenKey();
$updateUserSQL = "UPDATE user SET ctime = ".time().", ckey = '$userKey' WHERE user_id = $dbUserId;";
$shaUserKey = sha1($userKey);
$expire = $stayLoggedIn?30:0; // if "stay logged in", stay logged in for 30 days.
$updateUserSQL = "INSERT INTO user_login (user_id, ckey, ctime, expire, agent) VALUES ('$dbUserId', '$userKey', '".time()."', $expire, '$md5agent');";
if($db->query($updateUserSQL)){
$_SESSION['user_key'] = sha1($userKey);
$_SESSION['user_id'] = $dbUserId;
$_SESSION['user_name'] = $dbUserName;
$_SESSION['user_agent'] = $md5agent;
$_SESSION['user_key'] = $shaUserKey;
if($stayLoggedIn){
setcookie("auth", $shaUserKey.".".$dbUserId, time() + (30 * 24*60*60), "/", $_SERVER['HTTP_HOST'], isset($_SERVER['HTTPS']), true );
}
return true;
}
else {
@ -119,9 +123,46 @@ function loginUser($email, $pass) {
return $err;
}
function logoutUser(){
if(checkLogin()){
function loginFromAuth($auth): bool {
global $db;
$db = $db ?? database();
$md5agent = md5($_SERVER['HTTP_USER_AGENT']);
$cookieSplode = explode('.', $auth);
if(count($cookieSplode) == 2){
$tkey = $cookieSplode[0];
$tuid = $cookieSplode[1];
$verifyLoginCookieRes = $db->query("SELECT ul.user_id, ul.ckey, ul.ctime, ul.expire, ul.agent, u.full_name FROM user_login ul INNER JOIN user u ON ul.user_id = u.user_id WHERE ul.user_id = '$tuid' ORDER BY ctime DESC");
while($row = $verifyLoginCookieRes->fetch_assoc()){
if($row['expire'] == 0){ $row['expire'] = 1; }
if(
sha1($row['ckey']) == $tkey &&
$row['agent'] == $md5agent &&
($row['ctime']+($row['expire']*24*60*60) > time())
){
session_regenerate_id();
$_SESSION['user_id'] = $row['user_id'];
$_SESSION['user_name'] = $row['full_name'];
$_SESSION['user_agent'] = $md5agent;
$_SESSION['user_key'] = sha1($row['ckey']);
return true;
}
}
}
return false;
}
function logoutUser($everywhere = false){
if(checkLogin()){
global $db;
$logoutSql = "DELETE FROM user_login WHERE user_id = $_SESSION[user_id]";
if(!$everywhere){ $logoutSql .= " AND agent = '".md5($_SERVER['HTTP_USER_AGENT'])."'"; }
$db = $db ?? database();
$db->query($logoutSql);
}
// Delete session-cookie
@ -131,6 +172,8 @@ function logoutUser(){
$params["secure"], $params["httponly"]
);
setcookie('auth', '', time() - 42000, "/", $_SERVER['HTTP_HOST'], isset($_SERVER['HTTPS']), true );
// Finally, destroy the session.
session_destroy();
}
@ -147,7 +190,7 @@ function PwdGen($pass, $returnHashed = false): string {
return password_hash($pwd_peppered, PASSWORD_ARGON2ID, ['threads' => 2]);
}
function GenKey($length = 7): string{
function GenKey($length = 21): string{
$password = "";
$possible = "0123456789abcdefghijkmnopqrstuvwxyz";
@ -166,8 +209,8 @@ function GenKey($length = 7): string{
return $password;
}
function checkLogin(): bool{
global $db, $_SESSION;
function checkLogin(): bool {
global $db, $_SESSION, $_COOKIE;
if($db == null){
$db = database();
@ -177,16 +220,25 @@ function checkLogin(): bool{
session_start();
}
if(!isset($_SESSION['user_id'])){
return false;
if(isset($_SESSION['user_id'])){
$md5agent = md5($_SERVER['HTTP_USER_AGENT']);
if($md5agent == @$_SESSION['user_agent']){
$verifyLoginSessionRes = $db->query("SELECT expire, ctime, ckey FROM user_login WHERE user_id = ".$_SESSION['user_id']." and agent = '$md5agent'");
if($verifyLoginSessionRes->num_rows > 0){
while ($row = $verifyLoginSessionRes->fetch_assoc()){
if(
(sha1($row['ckey']) == $_SESSION['user_key']) &&
($row['expire'] == 0 || $row['ctime']+($row['expire']*24*60*60) > time())
){
return true;
}
}
}
}
}
if(md5($_SERVER['HTTP_USER_AGENT']) == @$_SESSION['user_agent']){
$verifyLoginRes = $db->query("SELECT ckey FROM user WHERE user_id = ".$_SESSION['user_id']);
list($cKey) = $verifyLoginRes->fetch_row();
if(sha1($cKey) == $_SESSION['user_key']){
return true;
}
if(isset($_COOKIE['auth']) && loginFromAuth($_COOKIE['auth'])){
return true;
}
unset($_SESSION['user_id']);

View File

@ -5,7 +5,7 @@ $msg = array();
$missingConfig = false;
if($_SERVER['HTTP_HOST'] != "localhost"){
if($_SERVER['REMOTE_ADDR'] != "127.0.0.1"){
$err[] = "You need to use this page from localhost";
$fatalErr = true;
}
@ -15,29 +15,50 @@ if($_SERVER['HTTP_HOST'] != "localhost"){
// CREATE TABLES
$sql = "CREATE OR REPLACE TABLE `user` (
`user_id` BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`md5_id` VARCHAR(200) UNIQUE,
`full_name` TINYTEXT,
`user_email` VARCHAR(220) NOT NULL UNIQUE,
`user_level` TINYINT(4) NOT NULL DEFAULT 1,
`pwd` VARCHAR(220),
`date` DATE NOT NULL DEFAULT(CURRENT_DATE),
`ckey` VARCHAR(220),
`ctime` VARCHAR(220)
`user_id` BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`md5_id` VARCHAR(200) UNIQUE,
`full_name` TINYTEXT,
`user_email` VARCHAR(220) NOT NULL UNIQUE,
`user_level` TINYINT(4) NOT NULL DEFAULT 1,
`pwd` VARCHAR(220),
`date` DATE NOT NULL DEFAULT(CURRENT_DATE)
) DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
CREATE OR REPLACE TABLE `user_login` (
user_id bigint not null,
ckey varchar(220) not null,
ctime varchar(220) not null,
expire smallint default 0 not null,
agent varchar(255) null,
primary key (user_id, ctime),
constraint user_id_FK foreign key (user_id) references user (user_id)
);
CREATE OR REPLACE TABLE `option` (
`option` varchar(100) not null primary key
);
CREATE OR REPLACE TABLE `user_option` (
user_id BIGINT(20) NOT NULL,
`option` varchar(100) NOT NULL,
value varchar(100) NOT NULL,
PRIMARY KEY (user_id,`option`),
CONSTRAINT user_options_FK FOREIGN KEY (user_id) REFERENCES `user`(user_id),
CONSTRAINT option_FK FOREIGN KEY (`option`) REFERENCES `option`(`option`)
)
CREATE OR REPLACE TABLE `plan_space` (
`space_id` INT auto_increment PRIMARY KEY,
`space_name` tinytext,
`owner_id` BIGINT(20),
`space_id` INT auto_increment PRIMARY KEY,
`space_name` tinytext,
`owner_id` BIGINT(20),
`space_type` enum('STORE','CHECK','CALORIES') NOT NULL DEFAULT 'STORE', # not sure if needed
CONSTRAINT plan_space_owner_FK FOREIGN KEY (`owner_id`) REFERENCES `user`(`user_id`)
);
CREATE OR REPLACE TABLE `plan_space_member` (
`space_id` INT auto_increment NOT NULL,
`member_id` BIGINT(20) NOT NULL,
`timestamp` DATETIME default current_timestamp() NOT NULL,
`space_id` INT auto_increment NOT NULL,
`member_id` BIGINT(20) NOT NULL,
`timestamp` DATETIME default current_timestamp() NOT NULL,
PRIMARY KEY (`space_id`, `member_id`),
CONSTRAINT space_member_FK FOREIGN KEY (`member_id`) REFERENCES `user`(`user_id`),
CONSTRAINT space_member_space_FK FOREIGN KEY (`space_id`) REFERENCES `plan_space`(`space_id`)
@ -45,22 +66,22 @@ CREATE OR REPLACE TABLE `plan_space_member` (
CREATE OR REPLACE TABLE `plan_store` (
`plan_store_id` INT auto_increment,
`space_id` INT NOT NULL,
`name` varchar(100) NOT NULL,
`created` DATETIME default current_timestamp() NOT NULL,
`state` ENUM('planning', 'shopping', 'closed') default 'planning',
`space_id` INT NOT NULL,
`name` varchar(100) NOT NULL,
`created` DATETIME default current_timestamp() NOT NULL,
`state` ENUM('planning', 'shopping', 'closed') default 'planning',
PRIMARY KEY (plan_store_id),
CONSTRAINT plan_store_user_FK FOREIGN KEY (`space_id`) REFERENCES `plan_space`(`space_id`)
) DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
CREATE OR REPLACE TABLE `plan_store_item` (
`plan_item_id` INT auto_increment NOT NULL,
`plan_store_id` INT NOT NULL,
`pos` tinyint(3) unsigned,
`name` varchar(200) NOT NULL,
`price` decimal(8,2) NOT NULL,
`amount` tinyint(3) unsigned DEFAULT 1,
`checked` BOOLEAN default 0,
`plan_item_id` INT auto_increment NOT NULL,
`plan_store_id` INT NOT NULL,
`pos` tinyint(3) unsigned,
`name` varchar(200) NOT NULL,
`price` decimal(8,2) NOT NULL,
`amount` tinyint(3) unsigned DEFAULT 1,
`checked` BOOLEAN default 0,
PRIMARY KEY (plan_item_id),
CONSTRAINT plan_store_item_FK FOREIGN KEY (plan_store_id) REFERENCES `plan_store`(`plan_store_id`)
) DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;