adding keepmeloggedin

This commit is contained in:
Janis Steiner
2024-12-28 17:15:57 +01:00
parent 052c8c4181
commit 5214c96b90
16 changed files with 293 additions and 38 deletions

View File

@@ -53,7 +53,7 @@ To integrate Jakach Login into your application:
4. **Add login button:** 4. **Add login button:**
In your app/login page add a button like: In your app/login page add a button like:
```html ```html
<a href="https://jakach.duckdns.org:444/?send_to=<your url/place of your oauth file>" class="btn btn-secondary">Log in using Jakach login</a> <a href="https://jakach-auth.duckdns.org:444/?send_to=<your url/place of your oauth file>" class="btn btn-secondary">Log in using Jakach login</a>
``` ```
--- ---

View File

@@ -54,6 +54,13 @@ if (!isset($_SESSION["logged_in"]) || $_SESSION["logged_in"] !== true) {
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<a class="nav-link" id="message-tab" data-bs-toggle="tab" href="#message" role="tab" aria-controls="message" aria-selected="false"><span class="material-icons">message</span></a> <a class="nav-link" id="message-tab" data-bs-toggle="tab" href="#message" role="tab" aria-controls="message" aria-selected="false"><span class="material-icons">message</span></a>
</li> </li>
<?php
if($_SESSION["permissions"][0]==="1"){
echo('<li class="nav-item" role="presentation">
<a class="nav-link" href="/account/manage_users.php" role="tab" aria-controls="message" aria-selected="false"><span class="material-icons">people</span></a>
</li>');
}
?>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<a class="nav-link" href="/login/logout.php" role="tab" aria-selected="false"><span class="material-icons">logout</span></a> <a class="nav-link" href="/login/logout.php" role="tab" aria-selected="false"><span class="material-icons">logout</span></a>
</li> </li>
@@ -96,6 +103,7 @@ if (!isset($_SESSION["logged_in"]) || $_SESSION["logged_in"] !== true) {
<!-- Save Changes Button --> <!-- Save Changes Button -->
<button type="button" id="save-button" class="btn btn-success">Save Changes</button> <button type="button" id="save-button" class="btn btn-success">Save Changes</button>
<a class="btn btn-danger" onclick="delete_all_logmein();">Delete all &quot;remember me&quot; sessions</a>
</form> </form>
</div> </div>
@@ -566,7 +574,9 @@ if (!isset($_SESSION["logged_in"]) || $_SESSION["logged_in"] !== true) {
location.href = location.href.replace('http', 'https'); location.href = location.href.replace('http', 'https');
} }
} }
function delete_all_logmein(){
fetch("/api/login/delete_keepmeloggedin.php");
}
</script> </script>
</body> </body>

View File

@@ -0,0 +1,15 @@
<?php
session_start();
header('Content-Type: application/json');
$send_to=$_SESSION["end_url"];
include "../../config/config.php";
$conn = new mysqli($DB_SERVERNAME, $DB_USERNAME, $DB_PASSWORD, $DB_DATABASE);
$user_id=$_SESSION["id"];
$sql="DELETE FROM keepmeloggedin WHERE user_id = ?";
$stmt = mysqli_prepare($conn, $sql);
mysqli_stmt_bind_param($stmt, 'i', $user_id);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
?>

View File

@@ -0,0 +1,36 @@
<?php
session_start();
header('Content-Type: application/json');
$send_to=$_SESSION["end_url"];
include "../../config/config.php";
$conn = new mysqli($DB_SERVERNAME, $DB_USERNAME, $DB_PASSWORD, $DB_DATABASE);
$keepmeloggedin=$_POST["keepmeloggedin"];
if($keepmeloggedin=="true"){
$_SESSION["keepmeloggedin_asked"]=true;
$user_id=$_SESSION["id"];
//create a login token
$login_token=bin2hex(random_bytes(128));
$agent=$_SERVER['HTTP_USER_AGENT'];
$sql="INSERT INTO keepmeloggedin (auth_token,user_id,agent) VALUES (?,?,?);";
$stmt = mysqli_prepare($conn, $sql);
mysqli_stmt_bind_param($stmt, 'sis', $login_token,$user_id,$agent);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
setcookie("auth_token", $login_token, time() + (30 * 24 * 60 * 60), "/", "", true, true);
$data = [
'status' => 'success'
];
echo(json_encode($data));
}else{
$_SESSION["keepmeloggedin_asked"]=true;
$data = [
'status' => 'success'
];
echo(json_encode($data));
}
?>

View File

@@ -2,31 +2,7 @@
session_start(); session_start();
header('Content-Type: application/json'); header('Content-Type: application/json');
include "../utils/get_location.php";
function get_location_from_ip($ip) {
// Use ip-api.com to fetch geolocation data
$url = "http://ip-api.com/json/$ip";
// Initialize curl
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute curl and decode the JSON response
$response = curl_exec($ch);
curl_close($ch);
// Convert JSON response to PHP array
$data = json_decode($response, true);
// Check for a successful response
if ($data && $data['status'] === 'success') {
return $data; // Return the geolocation data
}
return null; // Return null if API call fails
}
$send_to=$_SESSION["end_url"]; $send_to=$_SESSION["end_url"];
@@ -57,7 +33,16 @@ else if($_SESSION["needs_auth"]===false && $_SESSION["mfa_required"]==1 && $_SES
'redirect' => '/login/passkey.php' 'redirect' => '/login/passkey.php'
]; ];
echo(json_encode($data)); echo(json_encode($data));
}*/else if ($_SESSION["needs_auth"]===false && $_SESSION["mfa_authenticated"]==1 && $_SESSION["pw_authenticated"]==1){ }*/
else if ($_SESSION["needs_auth"]===false && $_SESSION["mfa_authenticated"]==1 && $_SESSION["pw_authenticated"]==1 && $_SESSION["keepmeloggedin_asked"]==false){
//send to keepmelogged in question
$data=[
'message' => 'ask_keepmeloggedin',
'redirect' => '/login/keepmeloggedin.php'
];
echo(json_encode($data));
}
else if ($_SESSION["needs_auth"]===false && $_SESSION["mfa_authenticated"]==1 && $_SESSION["pw_authenticated"]==1){
//fully authenticated //fully authenticated
//create auth token which other services can then use to check if user logged in //create auth token which other services can then use to check if user logged in
$user_id=$_SESSION["id"]; $user_id=$_SESSION["id"];
@@ -133,7 +118,7 @@ else{
$username=$_SESSION["username"]; $username=$_SESSION["username"];
$_SESSION["needs_auth"]=false; $_SESSION["needs_auth"]=false;
$_SESSION["logged_in"]=false; $_SESSION["logged_in"]=false;
$sql="SELECT auth_method_required_pw, auth_method_required_2fa, auth_method_required_passkey, id, user_token,last_login, login_message,telegram_id FROM users WHERE username = ?"; $sql="SELECT auth_method_required_pw, auth_method_required_2fa, auth_method_required_passkey, id, user_token,last_login, login_message,telegram_id, permissions FROM users WHERE username = ?";
$stmt = mysqli_prepare($conn, $sql); $stmt = mysqli_prepare($conn, $sql);
mysqli_stmt_bind_param($stmt, 's', $username); mysqli_stmt_bind_param($stmt, 's', $username);
mysqli_stmt_execute($stmt); mysqli_stmt_execute($stmt);
@@ -145,8 +130,9 @@ else{
$last_login=""; $last_login="";
$login_message=0; $login_message=0;
$telegram_id=""; $telegram_id="";
$permissions="";
if(mysqli_stmt_num_rows($stmt) == 1){ if(mysqli_stmt_num_rows($stmt) == 1){
mysqli_stmt_bind_result($stmt, $pw,$mfa,$passkey,$user_id,$user_token,$last_login,$login_message,$telegram_id); mysqli_stmt_bind_result($stmt, $pw,$mfa,$passkey,$user_id,$user_token,$last_login,$login_message,$telegram_id,$permissions);
mysqli_stmt_fetch($stmt); mysqli_stmt_fetch($stmt);
$_SESSION["pw_required"] = $pw; $_SESSION["pw_required"] = $pw;
$_SESSION["pw_authenticated"] = ($pw == 0) ? 1 : 0; // If $pw is 0, set pw_authenticated to 1 $_SESSION["pw_authenticated"] = ($pw == 0) ? 1 : 0; // If $pw is 0, set pw_authenticated to 1
@@ -159,6 +145,8 @@ else{
$_SESSION["last_login"]=$last_login; $_SESSION["last_login"]=$last_login;
$_SESSION["telegram_id"]=$telegram_id; $_SESSION["telegram_id"]=$telegram_id;
$_SESSION["login_message"]=$login_message; $_SESSION["login_message"]=$login_message;
$_SESSION["permissions"]=$permissions;
$_SESSION["keepmeloggedin_asked"]=false;
$data=[ $data=[
'message' => 'prepared_start_auth', 'message' => 'prepared_start_auth',
'redirect' => '/login/' 'redirect' => '/login/'

View File

@@ -53,7 +53,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
}else { }else {
mysqli_stmt_close($stmt); mysqli_stmt_close($stmt);
echo json_encode(['success' => false, 'message' => 'Ivalid auth token']); echo json_encode(['success' => false, 'message' => 'Invalid auth token']);
} }
//remove token //remove token
$sql="DELETE FROM reset_tokens WHERE auth_token = ?;"; $sql="DELETE FROM reset_tokens WHERE auth_token = ?;";

View File

@@ -2,6 +2,7 @@
session_start(); session_start();
header('Content-Type: application/json'); header('Content-Type: application/json');
include "../../config/config.php"; include "../../config/config.php";
include "../utils/get_location.php";
$username=$_SESSION["username"]; $username=$_SESSION["username"];
$sql="SELECT id, email, telegram_id FROM users WHERE username = ?;"; $sql="SELECT id, email, telegram_id FROM users WHERE username = ?;";
$conn = new mysqli($DB_SERVERNAME, $DB_USERNAME, $DB_PASSWORD, $DB_DATABASE); $conn = new mysqli($DB_SERVERNAME, $DB_USERNAME, $DB_PASSWORD, $DB_DATABASE);
@@ -18,9 +19,10 @@ mysqli_stmt_close($stmt);
//send telegram message //send telegram message
$device = $_SERVER['HTTP_USER_AGENT']; $device = $_SERVER['HTTP_USER_AGENT'];
$ip=$_SERVER["REMOTE_ADDR"]; $ip=$_SERVER["REMOTE_ADDR"];
$location=get_location_from_ip($ip);
$date=date('Y-m-d H:i:s'); $date=date('Y-m-d H:i:s');
$token=bin2hex(random_bytes(128)); $token=bin2hex(random_bytes(128));
$link="https://jakach.duckdns.org:444/login/reset_pw.php?token=$token"; $link="https://jakach-auth.duckdns.org:444/login/reset_pw.php?token=$token";
$message = "*Password reset token*\n\n" $message = "*Password reset token*\n\n"
. "You have requested the reset of your password here is your reset link.\n\n" . "You have requested the reset of your password here is your reset link.\n\n"
@@ -28,6 +30,7 @@ $message = "*Password reset token*\n\n"
. "*Details of this request:*\n" . "*Details of this request:*\n"
. "• *Date&Time*: $date\n" . "• *Date&Time*: $date\n"
. "• *Device&Browser*: $device\n" . "• *Device&Browser*: $device\n"
. "*Location*: ".$location["country"].", ".$location["state"].", ".$location["city"]."\n"
. "• *Account*: ".$_SESSION["username"]."\n" . "• *Account*: ".$_SESSION["username"]."\n"
. "• *IP*: $ip\n\n" . "• *IP*: $ip\n\n"
."If this was you, you can reset your password. If this was not you somebody else tried to reset your password!\n" ."If this was you, you can reset your password. If this was not you somebody else tried to reset your password!\n"
@@ -54,6 +57,7 @@ curl_exec($ch);
curl_close($ch); curl_close($ch);
//send mail //send mail
if(!empty($mail)){ if(!empty($mail)){
$loc=$location["country"].", ".$location["state"].", ".$location["city"];
$content = " $content = "
<!DOCTYPE html> <!DOCTYPE html>
<html lang='en'> <html lang='en'>
@@ -125,6 +129,7 @@ if(!empty($mail)){
<li><strong>Device & Browser:</strong> $device</li> <li><strong>Device & Browser:</strong> $device</li>
<li><strong>Account:</strong> $mail</li> <li><strong>Account:</strong> $mail</li>
<li><strong>IP Address:</strong> $ip</li> <li><strong>IP Address:</strong> $ip</li>
<li><strong>Location:</strong> $loc</li>
</ul> </ul>
<p>If this was you, you can reset your password. If this was not you, someone else may have tried to reset your password.</p> <p>If this was you, you can reset your password. If this was not you, someone else may have tried to reset your password.</p>
@@ -182,7 +187,7 @@ if(!empty($mail)){
//insert the token into our db //insert the token into our db
$valid_until=time()+8600; $valid_until=time()+(8600/2);
$sql="INSERT INTO reset_tokens (auth_token, user_id,valid_until) VALUES (?,?,?);"; $sql="INSERT INTO reset_tokens (auth_token, user_id,valid_until) VALUES (?,?,?);";
$stmt = mysqli_prepare($conn, $sql); $stmt = mysqli_prepare($conn, $sql);
mysqli_stmt_bind_param($stmt, 'sii', $token,$id,$valid_until); mysqli_stmt_bind_param($stmt, 'sii', $token,$id,$valid_until);

View File

@@ -0,0 +1,65 @@
<?php
function logmein(){
$ret="failure";
include "/var/www/html/config/config.php";
$conn = new mysqli($DB_SERVERNAME, $DB_USERNAME, $DB_PASSWORD, $DB_DATABASE);
if (isset($_COOKIE['auth_token'])) {
$auth_token=$_COOKIE['auth_token'];
$sql="SELECT user_id,agent FROM keepmeloggedin WHERE auth_token = ?";
$user_id=0;
$agent="";
$stmt = mysqli_prepare($conn, $sql);
mysqli_stmt_bind_param($stmt, 's',$auth_token);
mysqli_stmt_execute($stmt);
mysqli_stmt_store_result($stmt);
if(mysqli_stmt_num_rows($stmt) == 1){
mysqli_stmt_bind_result($stmt, $user_id,$agent);
mysqli_stmt_fetch($stmt);
mysqli_stmt_close($stmt);
//load user data
$sql="SELECT auth_method_required_pw, auth_method_required_2fa, auth_method_required_passkey, username, user_token,last_login, login_message,telegram_id, permissions FROM users WHERE id = ?";
$stmt = mysqli_prepare($conn, $sql);
mysqli_stmt_bind_param($stmt, 'i', $user_id);
mysqli_stmt_execute($stmt);
mysqli_stmt_store_result($stmt);
$pw=0;
$username="";
$mfa=0;
$passkey=0;
$user_token="";
$last_login="";
$login_message=0;
$telegram_id="";
$permissions="";
if(mysqli_stmt_num_rows($stmt) == 1){
mysqli_stmt_bind_result($stmt, $pw,$mfa,$passkey,$username,$user_token,$last_login,$login_message,$telegram_id,$permissions);
mysqli_stmt_fetch($stmt);
$_SESSION["pw_required"] = $pw;
$_SESSION["pw_authenticated"] = 1;
$_SESSION["mfa_required"] = $mfa;
$_SESSION["mfa_authenticated"] = 1;
$_SESSION["passkey_required"] = $passkey;
$_SESSION["passkey_authenticated"] = 1;
$_SESSION["id"]=$user_id;
$_SESSION["username"]=$username;
$_SESSION["user_token"]=$user_token;
$_SESSION["last_login"]=$last_login;
$_SESSION["telegram_id"]=$telegram_id;
//$_SESSION["login_message"]=$login_message;
$_SESSION["login_message"]=false; // do not send a message if logged in via keepmeloggedin
$_SESSION["permissions"]=$permissions;
$_SESSION["keepmeloggedin_asked"]=true;
$_SESSION["logged_in"]=true;
$_SESSION["needs_auth"]=false;
$ret="success";
}
mysqli_stmt_close($stmt);
}else{
mysqli_stmt_close($stmt);
}
}
return $ret;
}
?>

View File

@@ -0,0 +1,27 @@
<?php
function get_location_from_ip($ip) {
// Use ip-api.com to fetch geolocation data
$url = "http://ip-api.com/json/$ip";
// Initialize curl
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute curl and decode the JSON response
$response = curl_exec($ch);
curl_close($ch);
// Convert JSON response to PHP array
$data = json_decode($response, true);
// Check for a successful response
if ($data && $data['status'] === 'success') {
return $data; // Return the geolocation data
}
return null; // Return null if API call fails
}
?>

View File

@@ -8,9 +8,17 @@
<title>Jakach Login</title> <title>Jakach Login</title>
<?php <?php
include "assets/components.php"; include "assets/components.php";
include "api/utils/check_keepmeloggedin.php";
session_start(); session_start();
$_SESSION["end_url"]=$_GET["send_to"]; $_SESSION["end_url"]=$_GET["send_to"];
if (isset($_SESSION["logged_in"]) && $_SESSION["logged_in"] === true && !isset($_GET["donotsend"])) { if (isset($_SESSION["logged_in"]) && $_SESSION["logged_in"] === true && !isset($_GET["donotsend"]) /*also check for keepmeloggedin here*/) {
header("LOCATION:/login/account_selector.php");
exit();
}
if(isset($_GET["donotsend"])){
setcookie("auth_token", "", time() - 3600, "/");
}
if(logmein()==="success"){
header("LOCATION:/login/account_selector.php"); header("LOCATION:/login/account_selector.php");
exit(); exit();
} }
@@ -41,6 +49,8 @@
<button type="submit" class="btn btn-primary btn-lg">Einloggen</button> <button type="submit" class="btn btn-primary btn-lg">Einloggen</button>
<!-- Register Button --> <!-- Register Button -->
<a class="btn btn-outline-primary btn-lg" href="/register/">Account Erstellen</a> <a class="btn btn-outline-primary btn-lg" href="/register/">Account Erstellen</a>
<br>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -74,7 +74,6 @@
permissions VARCHAR(255), permissions VARCHAR(255),
color_profile INT, color_profile INT,
auth_key VARCHAR(255), auth_key VARCHAR(255),
keepmeloggedin_token VARCHAR(255),
auth_method_keepmeloggedin_enabled INT, auth_method_keepmeloggedin_enabled INT,
auth_method_enabled_2fa INT, auth_method_enabled_2fa INT,
auth_method_enabled_pw INT, auth_method_enabled_pw INT,
@@ -135,6 +134,25 @@
</div>'; </div>';
} }
$sql="CREATE TABLE IF NOT EXISTS keepmeloggedin (
id INT AUTO_INCREMENT PRIMARY KEY,
auth_token VARCHAR(256),
user_id INT,
agent VARCHAR(255)
);";
if ($conn->query($sql) === TRUE) {
echo '<br><div class="alert alert-success" role="alert">
Table keepmeloggedin created successfully!
</div>';
} else {
$success=0;
echo '<br><div class="alert alert-danger" role="alert">
Error creating keepmeloggedin users: ' . $conn->error .'
</div>';
}
if($success!==1){ if($success!==1){

BIN
app-code/jakach_logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jakach Login</title>
<?php
include "../assets/components.php";
session_start();
?>
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6 col-lg-4">
<!-- Card for the form -->
<div class="card shadow">
<div class="card-body">
<h4 class="card-title text-center mb-4">Jakach Login</h4>
<!-- Form -->
<form id="twofaForm">
<!-- Submit Button -->
<div class="d-grid gap-2">
<!-- Login Button -->
<a class="btn btn-primary btn-lg" onclick="send_request('true');">Remember me</a>
<a class="btn btn-primary btn-lg" onclick="send_request('false');">Do not remember me</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
// Select the form
async function send_request(keepmeloggedin){
const response = await fetch('/api/login/keepmeloggedin.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded', // Form-like data
},
body: new URLSearchParams({
keepmeloggedin: keepmeloggedin, // Send password as form data
}),
});
if (response.ok) {
// Await response.json() to parse the JSON
const data = await response.json();
if (data.status === 'success') {
// Redirect to /login/
window.location.href = '/login/';
} else {
// Show the error modal on failure
var errorModal = new bootstrap.Modal(document.getElementById('errorModal'));
errorModal.show();
// Optionally, display the error message
console.error("Error: ", data.message || "Unknown error");
}
}
}
</script>
</body>
</html>

View File

@@ -2,5 +2,6 @@
session_start(); session_start();
session_unset(); session_unset();
session_destroy(); session_destroy();
setcookie("auth_token", "", time() - 3600, "/");
header("LOCATION:/"); header("LOCATION:/");
?> ?>

View File

@@ -22,7 +22,9 @@
<!-- Submit Button --> <!-- Submit Button -->
<div class="d-grid gap-2"> <div class="d-grid gap-2">
<!-- Login Button --> <!-- Login Button -->
<button style="align:right" type="button" class="btn btn-primary btn-block" onclick="checkRegistration()">Login with a passkey</button> <button id="loginButton" type="button" class="btn btn-primary btn-block" onclick="checkRegistration()">
<span id="buttonText">Login with a passkey</span>
</button>
</div> </div>
</form> </form>
</div> </div>
@@ -58,6 +60,15 @@ function showErrorModal(message) {
errorModal.show(); errorModal.show();
} }
async function checkRegistration() { async function checkRegistration() {
const button = document.getElementById("loginButton");
const buttonText = document.getElementById("buttonText");
// Disable the button to prevent multiple clicks
button.disabled = true;
// Change the button text and add a loading spinner
buttonText.innerHTML = 'Loading...';
button.innerHTML += ' <div class="spinner-border spinner-border-sm text-light" role="status"><span class="visually-hidden">Loading...</span></div>';
try { try {
if (!window.fetch || !navigator.credentials || !navigator.credentials.create) { if (!window.fetch || !navigator.credentials || !navigator.credentials.create) {

View File

@@ -6,7 +6,7 @@ This file can be installed in any service. If done so a user can authenticate wi
$auth_token = $_GET["auth"]; $auth_token = $_GET["auth"];
// Check the auth token against Jakach login API // Check the auth token against Jakach login API
$check_url = "https://jakach.duckdns.org:444/api/auth/check_auth_key.php?auth_token=" . $auth_token; $check_url = "https://jakach-auth.duckdns.org:444/api/auth/check_auth_key.php?auth_token=" . $auth_token;
// Initialize cURL // Initialize cURL
$ch = curl_init(); $ch = curl_init();