finishing touches for passkey auth, oauth and more

This commit is contained in:
Janis Steiner
2024-12-26 13:12:24 +01:00
parent e8cba3edf6
commit 301c9493b1
9 changed files with 232 additions and 173 deletions

View File

@@ -280,17 +280,6 @@ if (!isset($_SESSION["logged_in"]) || $_SESSION["logged_in"] !== true) {
}); });
}); });
// Function to show error modal
function showErrorModal(message) {
document.getElementById('errorModalMessage').textContent = message;
const errorModal = new bootstrap.Modal(document.getElementById('errorModal'));
errorModal.show();
}
function showSuccessModal(message) {
document.getElementById('successModalMessage').textContent = message;
const errorModal = new bootstrap.Modal(document.getElementById('successModal'));
errorModal.show();
}
//////////////////////////////////////////// ////////////////////////////////////////////
@@ -328,6 +317,18 @@ if (!isset($_SESSION["logged_in"]) || $_SESSION["logged_in"] !== true) {
} }
}); });
}); });
// Function to show error modal
function showErrorModal(message) {
document.getElementById('errorModalMessage').textContent = message;
const errorModal = new bootstrap.Modal(document.getElementById('errorModal'));
errorModal.show();
}
function showSuccessModal(message) {
document.getElementById('successModalMessage').textContent = message;
const errorModal = new bootstrap.Modal(document.getElementById('successModal'));
errorModal.show();
}
//webauthn js //webauthn js
async function createRegistration() { async function createRegistration() {
@@ -374,34 +375,17 @@ if (!isset($_SESSION["logged_in"]) || $_SESSION["logged_in"] !== true) {
// prompt server response // prompt server response
if (authenticatorAttestationServerResponse.success) { if (authenticatorAttestationServerResponse.success) {
reloadServerPreview(); reloadServerPreview();
window.alert(authenticatorAttestationServerResponse.msg || 'registration success'); showSuccessModal(authenticatorAttestationServerResponse.msg || 'Registrated passkey successfully');
} else { } else {
throw new Error(authenticatorAttestationServerResponse.msg); throw new Error(authenticatorAttestationServerResponse.msg);
} }
} catch (err) { } catch (err) {
reloadServerPreview(); reloadServerPreview();
window.alert(err.message || 'unknown error occured'); showErrorModal(err.message || 'unknown error occured');
} }
} }
function queryFidoMetaDataService() {
window.fetch('/api/account/update_passkey.php?fn=queryFidoMetaDataService' + getGetParams(), {method:'GET',cache:'no-cache'}).then(function(response) {
return response.json();
}).then(function(json) {
if (json.success) {
window.alert(json.msg);
} else {
throw new Error(json.msg);
}
}).catch(function(err) {
window.alert(err.message || 'unknown error occured');
});
}
/** /**
* convert RFC 1342-like base64 strings to array buffer * convert RFC 1342-like base64 strings to array buffer
* @param {mixed} obj * @param {mixed} obj

View File

@@ -47,27 +47,13 @@ try {
// Formats // Formats
$formats = []; $formats = [];
//if (filter_input(INPUT_GET, 'fmt_android-key')) {
$formats[] = 'android-key'; $formats[] = 'android-key';
//}
///if (filter_input(INPUT_GET, 'fmt_android-safetynet')) {
$formats[] = 'android-safetynet'; $formats[] = 'android-safetynet';
//}
//if (filter_input(INPUT_GET, 'fmt_apple')) {
$formats[] = 'apple'; $formats[] = 'apple';
//}
//if (filter_input(INPUT_GET, 'fmt_fido-u2f')) {
$formats[] = 'fido-u2f'; $formats[] = 'fido-u2f';
//}
//if (filter_input(INPUT_GET, 'fmt_none')) {
$formats[] = 'none'; $formats[] = 'none';
//}
//if (filter_input(INPUT_GET, 'fmt_packed')) {
$formats[] = 'packed'; $formats[] = 'packed';
//}
//if (filter_input(INPUT_GET, 'fmt_tpm')) {
$formats[] = 'tpm'; $formats[] = 'tpm';
//}
$rpId=$_SERVER['SERVER_NAME']; $rpId=$_SERVER['SERVER_NAME'];
@@ -94,28 +80,14 @@ try {
$WebAuthn = new lbuchs\WebAuthn\WebAuthn('WebAuthn Library', $rpId, $formats); $WebAuthn = new lbuchs\WebAuthn\WebAuthn('WebAuthn Library', $rpId, $formats);
// add root certificates to validate new registrations // add root certificates to validate new registrations
//if (filter_input(INPUT_GET, 'solo')) {
$WebAuthn->addRootCertificates('rootCertificates/solo.pem'); $WebAuthn->addRootCertificates('rootCertificates/solo.pem');
//}
//if (filter_input(INPUT_GET, 'apple')) {
$WebAuthn->addRootCertificates('rootCertificates/apple.pem'); $WebAuthn->addRootCertificates('rootCertificates/apple.pem');
//}
//if (filter_input(INPUT_GET, 'yubico')) {
$WebAuthn->addRootCertificates('rootCertificates/yubico.pem'); $WebAuthn->addRootCertificates('rootCertificates/yubico.pem');
//}
//if (filter_input(INPUT_GET, 'hypersecu')) {
$WebAuthn->addRootCertificates('rootCertificates/hypersecu.pem'); $WebAuthn->addRootCertificates('rootCertificates/hypersecu.pem');
//}
//if (filter_input(INPUT_GET, 'google')) {
$WebAuthn->addRootCertificates('rootCertificates/globalSign.pem'); $WebAuthn->addRootCertificates('rootCertificates/globalSign.pem');
$WebAuthn->addRootCertificates('rootCertificates/googleHardware.pem'); $WebAuthn->addRootCertificates('rootCertificates/googleHardware.pem');
//}
//if (filter_input(INPUT_GET, 'microsoft')) {
$WebAuthn->addRootCertificates('rootCertificates/microsoftTpmCollection.pem'); $WebAuthn->addRootCertificates('rootCertificates/microsoftTpmCollection.pem');
//}
//if (filter_input(INPUT_GET, 'mds')) {
$WebAuthn->addRootCertificates('rootCertificates/mds'); $WebAuthn->addRootCertificates('rootCertificates/mds');
//}
} }
@@ -177,8 +149,6 @@ try {
// Store registration data in the database // Store registration data in the database
$stmt = $conn->prepare("UPDATE users set credential_id = ?, public_key = ?, counter = ?, auth_method_enabled_passkey = 1, auth_method_required_passkey = 1 WHERE username = ?"); $stmt = $conn->prepare("UPDATE users set credential_id = ?, public_key = ?, counter = ?, auth_method_enabled_passkey = 1, auth_method_required_passkey = 1 WHERE username = ?");
//$stmt = $conn->prepare("INSERT INTO users (user_hex_id, credential_id, public_key, counter) VALUES (?, ?, ?, ?)");
//var_dump($data);
$stmt->execute([ $data->credentialId, $data->credentialPublicKey, $data->signatureCounter,$userName]); $stmt->execute([ $data->credentialId, $data->credentialPublicKey, $data->signatureCounter,$userName]);
$msg = 'registration success.'; $msg = 'registration success.';
@@ -198,3 +168,4 @@ try {
print(json_encode($return)); print(json_encode($return));
} }
?> ?>

View File

@@ -0,0 +1,55 @@
<?php
header('Content-Type: application/json');
include "../../config/config.php";
$conn = new mysqli($DB_SERVERNAME, $DB_USERNAME, $DB_PASSWORD, $DB_DATABASE);
$auth_key=$_GET["auth_token"];
$sql="SELECT user_id FROM auth_tokens WHERE auth_token = ?;";
$stmt = mysqli_prepare($conn, $sql);
mysqli_stmt_bind_param($stmt, 's', $auth_key);
mysqli_stmt_execute($stmt);
mysqli_stmt_store_result($stmt);
//if auth key is valid
if(mysqli_stmt_num_rows($stmt) == 1){
$user_id=0;
mysqli_stmt_bind_result($stmt,$user_id);
mysqli_stmt_fetch($stmt);
//we now have userid, close stmt
mysqli_stmt_close($stmt);
$sql="SELECT username, email, telegram_id 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);
$username="";
$email="";
$telegram="";
mysqli_stmt_bind_result($stmt,$username,$email,$telegram);
mysqli_stmt_fetch($stmt);
mysqli_stmt_close($stmt);
$data=[
'status'=>'success',
'msg'=>'user authenticated',
'username'=>$username,
'email'=>$email,
'telegram_id'=>$telegram,
'id'=>$user_id
];
//remove auth key
$sql="DELETE FROM auth_tokens WHERE auth_token = ?;";
$stmt = mysqli_prepare($conn, $sql);
mysqli_stmt_bind_param($stmt, 's', $auth_key);
mysqli_stmt_execute($stmt);
echo(json_encode($data));
}else{
$data=[
'status' => 'failure',
'msg'=>'invalid auth key',
'auth_key'=>$auth_key
];
echo(json_encode($data));
}
?>

View File

@@ -33,27 +33,13 @@ try {
// Formats // Formats
$formats = []; $formats = [];
//if (filter_input(INPUT_GET, 'fmt_android-key')) {
$formats[] = 'android-key'; $formats[] = 'android-key';
//}
///if (filter_input(INPUT_GET, 'fmt_android-safetynet')) {
$formats[] = 'android-safetynet'; $formats[] = 'android-safetynet';
//}
//if (filter_input(INPUT_GET, 'fmt_apple')) {
$formats[] = 'apple'; $formats[] = 'apple';
//}
//if (filter_input(INPUT_GET, 'fmt_fido-u2f')) {
$formats[] = 'fido-u2f'; $formats[] = 'fido-u2f';
//}
//if (filter_input(INPUT_GET, 'fmt_none')) {
$formats[] = 'none'; $formats[] = 'none';
//}
//if (filter_input(INPUT_GET, 'fmt_packed')) {
$formats[] = 'packed'; $formats[] = 'packed';
//}
//if (filter_input(INPUT_GET, 'fmt_tpm')) {
$formats[] = 'tpm'; $formats[] = 'tpm';
//}
$rpId=$_SERVER['SERVER_NAME']; $rpId=$_SERVER['SERVER_NAME'];
@@ -80,28 +66,14 @@ try {
$WebAuthn = new lbuchs\WebAuthn\WebAuthn('WebAuthn Library', $rpId, $formats); $WebAuthn = new lbuchs\WebAuthn\WebAuthn('WebAuthn Library', $rpId, $formats);
// add root certificates to validate new registrations // add root certificates to validate new registrations
//if (filter_input(INPUT_GET, 'solo')) {
$WebAuthn->addRootCertificates('rootCertificates/solo.pem'); $WebAuthn->addRootCertificates('rootCertificates/solo.pem');
//}
//if (filter_input(INPUT_GET, 'apple')) {
$WebAuthn->addRootCertificates('rootCertificates/apple.pem'); $WebAuthn->addRootCertificates('rootCertificates/apple.pem');
//}
//if (filter_input(INPUT_GET, 'yubico')) {
$WebAuthn->addRootCertificates('rootCertificates/yubico.pem'); $WebAuthn->addRootCertificates('rootCertificates/yubico.pem');
//}
//if (filter_input(INPUT_GET, 'hypersecu')) {
$WebAuthn->addRootCertificates('rootCertificates/hypersecu.pem'); $WebAuthn->addRootCertificates('rootCertificates/hypersecu.pem');
//}
//if (filter_input(INPUT_GET, 'google')) {
$WebAuthn->addRootCertificates('rootCertificates/globalSign.pem'); $WebAuthn->addRootCertificates('rootCertificates/globalSign.pem');
$WebAuthn->addRootCertificates('rootCertificates/googleHardware.pem'); $WebAuthn->addRootCertificates('rootCertificates/googleHardware.pem');
//}
//if (filter_input(INPUT_GET, 'microsoft')) {
$WebAuthn->addRootCertificates('rootCertificates/microsoftTpmCollection.pem'); $WebAuthn->addRootCertificates('rootCertificates/microsoftTpmCollection.pem');
//}
//if (filter_input(INPUT_GET, 'mds')) {
$WebAuthn->addRootCertificates('rootCertificates/mds'); $WebAuthn->addRootCertificates('rootCertificates/mds');
//}
} }

View File

@@ -23,20 +23,35 @@ else if($_SESSION["needs_auth"]===false && $_SESSION["mfa_required"]==1 && $_SES
//check for mfa //check for mfa
} }
else if($_SESSION["needs_auth"]===false && $_SESSION["passkey_required"]==1 && $_SESSION["passkey_authenticated"]==0){ /*else if($_SESSION["needs_auth"]===false && $_SESSION["passkey_required"]==1 && $_SESSION["passkey_authenticated"]==0){
//check for passkey //check for passkey
$data=[ $data=[
'message' => 'auth_passkey', 'message' => 'auth_passkey',
'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 && $_SESSION["passkey_authenticated"]){ }*/else if ($_SESSION["needs_auth"]===false && $_SESSION["mfa_authenticated"]==1 && $_SESSION["pw_authenticated"]==1){
//fully authenticated //fully authenticated
$_SESSION["logged_in"]=true; $_SESSION["logged_in"]=true;
//create auth token which other services can then use to check if user logged in
$user_id=$_SESSION["id"];
$auth_token=bin2hex(random_bytes(128));
$sql="INSERT INTO auth_tokens (auth_token,user_id) VALUES(?,?);";
$stmt = mysqli_prepare($conn, $sql);
mysqli_stmt_bind_param($stmt, 'si', $auth_token,$user_id);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
if(!empty($send_to)){
$data=[ $data=[
'message' => 'done', 'message' => 'done',
'redirect' => $send_to 'redirect' => $send_to."?auth=$auth_token"
]; ];
}else{
$data=[
'message' => 'done',
'redirect' => ''
];
}
echo(json_encode($data)); echo(json_encode($data));
} }
else{ else{
@@ -45,7 +60,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 FROM users WHERE username = ?"; $sql="SELECT auth_method_required_pw, auth_method_required_2fa, auth_method_required_passkey, id 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);
@@ -54,7 +69,7 @@ else{
$mfa=0; $mfa=0;
$passkey=0; $passkey=0;
if(mysqli_stmt_num_rows($stmt) == 1){ if(mysqli_stmt_num_rows($stmt) == 1){
mysqli_stmt_bind_result($stmt, $pw,$mfa,$passkey); mysqli_stmt_bind_result($stmt, $pw,$mfa,$passkey,$user_id);
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
@@ -62,6 +77,7 @@ else{
$_SESSION["mfa_authenticated"] = ($mfa == 0) ? 1 : 0; $_SESSION["mfa_authenticated"] = ($mfa == 0) ? 1 : 0;
$_SESSION["passkey_required"] = $passkey; $_SESSION["passkey_required"] = $passkey;
$_SESSION["passkey_authenticated"] = ($passkey == 0) ? 1 : 0; $_SESSION["passkey_authenticated"] = ($passkey == 0) ? 1 : 0;
$_SESSION["id"]=$user_id;
$data=[ $data=[
'message' => 'prepared_start_auth', 'message' => 'prepared_start_auth',
'redirect' => '/login/' 'redirect' => '/login/'

View File

@@ -10,6 +10,10 @@
include "assets/components.php"; include "assets/components.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) {
header("LOCATION:/login/");
exit();
}
?> ?>
</head> </head>
<body> <body>

View File

@@ -61,7 +61,7 @@
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE, username VARCHAR(255) NOT NULL UNIQUE,
public_key TEXT DEFAULT '', public_key TEXT DEFAULT '',
credential_id VARBINARY(64), credential_id VARBINARY(255),
counter INT DEFAULT 0, counter INT DEFAULT 0,
2fa VARCHAR(255), 2fa VARCHAR(255),
email VARCHAR(255), email VARCHAR(255),
@@ -95,6 +95,26 @@
$sql="CREATE TABLE IF NOT EXISTS auth_tokens (
id INT AUTO_INCREMENT PRIMARY KEY,
auth_token VARCHAR(256),
user_id INT
);";
if ($conn->query($sql) === TRUE) {
echo '<br><div class="alert alert-success" role="alert">
Table auth_tokens created successfully!
</div>';
} else {
$success=0;
echo '<br><div class="alert alert-danger" role="alert">
Error creating auth_tokens users: ' . $conn->error .'
</div>';
}
if($success!==1){ if($success!==1){
echo '<br><div class="alert alert-danger" role="alert"> echo '<br><div class="alert alert-danger" role="alert">
There was an error creating the databases. Please try again or contact support at: <a href="mailto:info.jakach@gmail.com">info.jakach@gmail.com</a> There was an error creating the databases. Please try again or contact support at: <a href="mailto:info.jakach@gmail.com">info.jakach@gmail.com</a>

View File

@@ -31,15 +31,16 @@
</div> </div>
</div> </div>
<div class="modal fade" id="errorModal" tabindex="-1" aria-labelledby="errorModalLabel" aria-hidden="true"> <!-- Error Modal -->
<div class="modal fade" id="errorModal" tabindex="-1" aria-labelledby="errorModalLabel" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="errorModalLabel">Error</h5> <h5 class="modal-title" id="errorModalLabel">Error</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body" id="errorModalMessage">
Wrong 2fa pin! <!-- Error message will go here -->
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
@@ -50,8 +51,12 @@
<script> <script>
//js that handelsour passkey login //js that handels our passkey login
function showErrorModal(message) {
document.getElementById('errorModalMessage').textContent = message;
const errorModal = new bootstrap.Modal(document.getElementById('errorModal'));
errorModal.show();
}
async function checkRegistration() { async function checkRegistration() {
try { try {
@@ -96,8 +101,6 @@ async function checkRegistration() {
if (authenticatorAttestationServerResponse.success) { if (authenticatorAttestationServerResponse.success) {
reloadServerPreview(); reloadServerPreview();
//window.alert(authenticatorAttestationServerResponse.msg || 'login success'); //window.alert(authenticatorAttestationServerResponse.msg || 'login success');
//auth success, send to index
window.location.href = "/login/"; window.location.href = "/login/";
} else { } else {
@@ -108,29 +111,13 @@ async function checkRegistration() {
reloadServerPreview(); reloadServerPreview();
if(err.message=="User does not exist"){ if(err.message=="User does not exist"){
//we will display a warning here later on //we will display a warning here later on
//alert("User does not exist!!! check line 71 of login.html to set warning"); showErrorModal("User does not exist!");
var alert_message=document.getElementById("no_passkey");
alert_message.style.display="block";
}else{ }else{
window.alert(err.message || 'unknown error occured'); showErrorModal(err.message || 'unknown error occured');
} }
} }
} }
function queryFidoMetaDataService() {
window.fetch('/api/login/check_passkey.php?fn=queryFidoMetaDataService' + getGetParams(), {method:'GET',cache:'no-cache'}).then(function(response) {
return response.json();
}).then(function(json) {
if (json.success) {
window.alert(json.msg);
} else {
throw new Error(json.msg);
}
}).catch(function(err) {
window.alert(err.message || 'unknown error occured');
});
}
/** /**
* convert RFC 1342-like base64 strings to array buffer * convert RFC 1342-like base64 strings to array buffer

50
app-code/plugins/auth.php Normal file
View File

@@ -0,0 +1,50 @@
<?php
/*
This file can be installed in any service. If done so a user can authenticate with Jakach Auth. Jakach Auth will redirect the user here where their token gets validated, and then they can be logged in to your service.
*/
$auth_token = $_GET["auth"];
// 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;
// Initialize cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $check_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute cURL and get the response
$response = curl_exec($ch);
// Check for cURL errors
if(curl_errno($ch)) {
die("cURL Error: " . curl_error($ch));
}
// Close cURL
curl_close($ch);
// Decode the JSON response
$data = json_decode($response, true);
// Check if the response contains a valid status
if (isset($data['status'])) {
if ($data['status'] == "success") {
// Successful authentication: login the user
session_start();
$_SESSION["username"] = $data["username"];
$_SESSION["id"] = $data["id"];
$_SESSION["email"] = $data["email"];
$_SESSION["telegram_id"] = $data["telegram_id"];
// Return a success response
echo json_encode(['status' => 'success', 'msg' => 'logged in']);
} else {
// Authentication failed
echo json_encode(['status' => 'failure', 'msg' => $data["msg"]]);
}
} else {
// Invalid response format or missing status
echo json_encode(['status' => 'failure', 'msg' => 'Invalid response from authentication server']);
}
?>