Files
FileRise/totp_setup.php

167 lines
5.1 KiB
PHP

<?php
// totp_setup.php
require_once 'vendor/autoload.php';
require_once 'config.php';
use Endroid\QrCode\Builder\Builder;
use Endroid\QrCode\Writer\PngWriter;
use RobThree\Auth\Algorithm;
use RobThree\Auth\Providers\Qr\GoogleChartsQrCodeProvider;
// Define the respond() helper if not already defined.
if (!function_exists('respond')) {
function respond($status, $code, $message, $data = []) {
http_response_code($code);
echo json_encode([
'status' => $status,
'code' => $code,
'message' => $message,
'data' => $data
]);
exit;
}
}
// Allow access if the user is authenticated or pending TOTP.
if (!((isset($_SESSION['authenticated']) && $_SESSION['authenticated'] === true) || isset($_SESSION['pending_login_user']))) {
http_response_code(403);
exit;
}
// Retrieve CSRF token from GET parameter or request headers.
$headers = array_change_key_case(getallheaders(), CASE_LOWER);
$receivedToken = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : '';
if ($receivedToken !== $_SESSION['csrf_token']) {
echo json_encode(["error" => "Invalid CSRF token"]);
http_response_code(403);
exit;
}
$username = $_SESSION['username'] ?? '';
if (!$username) {
http_response_code(400);
exit;
}
// Set header to output a PNG image.
header("Content-Type: image/png");
// Define the path to your users.txt file.
$usersFile = USERS_DIR . USERS_FILE;
/**
* Updates the TOTP secret for the given user in users.txt.
*
* @param string $username
* @param string $encryptedSecret The encrypted TOTP secret.
*/
function updateUserTOTPSecret($username, $encryptedSecret) {
global $usersFile;
$lines = file($usersFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$newLines = [];
foreach ($lines as $line) {
$parts = explode(':', trim($line));
if (count($parts) < 3) {
$newLines[] = $line;
continue;
}
if ($parts[0] === $username) {
// If a fourth field exists, update it; otherwise, append it.
if (count($parts) >= 4) {
$parts[3] = $encryptedSecret;
} else {
$parts[] = $encryptedSecret;
}
$newLines[] = implode(':', $parts);
} else {
$newLines[] = $line;
}
}
file_put_contents($usersFile, implode(PHP_EOL, $newLines) . PHP_EOL, LOCK_EX);
}
/**
* Retrieves the current user's TOTP secret from users.txt (if present).
*
* @param string $username
* @return string|null The decrypted TOTP secret or null if not found.
*/
function getUserTOTPSecret($username) {
global $usersFile, $encryptionKey;
if (!file_exists($usersFile)) {
return null;
}
$lines = file($usersFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
$parts = explode(':', trim($line));
if (count($parts) >= 4 && $parts[0] === $username && !empty($parts[3])) {
return decryptData($parts[3], $encryptionKey);
}
}
return null;
}
/**
* Retrieves the global OTPAuth URL from admin configuration.
*
* @return string Global OTPAuth URL template or an empty string if not set.
*/
function getGlobalOtpauthUrl() {
global $encryptionKey;
$adminConfigFile = USERS_DIR . 'adminConfig.json';
if (file_exists($adminConfigFile)) {
$encryptedContent = file_get_contents($adminConfigFile);
$decryptedContent = decryptData($encryptedContent, $encryptionKey);
if ($decryptedContent !== false) {
$config = json_decode($decryptedContent, true);
if (isset($config['globalOtpauthUrl']) && !empty($config['globalOtpauthUrl'])) {
return $config['globalOtpauthUrl'];
}
}
}
return "";
}
$tfa = new \RobThree\Auth\TwoFactorAuth(
new GoogleChartsQrCodeProvider(), // QR code provider
'FileRise', // issuer
6, // number of digits
30, // period in seconds
Algorithm::Sha1 // enum case from your Algorithm enum
);
// Retrieve the current TOTP secret for the user.
$totpSecret = getUserTOTPSecret($username);
if (!$totpSecret) {
// If no TOTP secret exists, generate a new one.
$totpSecret = $tfa->createSecret();
$encryptedSecret = encryptData($totpSecret, $encryptionKey);
updateUserTOTPSecret($username, $encryptedSecret);
}
// Determine the otpauth URL to use.
$globalOtpauthUrl = getGlobalOtpauthUrl();
if (!empty($globalOtpauthUrl)) {
$label = "FileRise:" . $username;
$otpauthUrl = str_replace(
["{label}", "{secret}"],
[urlencode($label), $totpSecret],
$globalOtpauthUrl
);
} else {
$label = urlencode("FileRise:" . $username);
$issuer = urlencode("FileRise");
$otpauthUrl = "otpauth://totp/{$label}?secret={$totpSecret}&issuer={$issuer}";
}
// Build the QR code using Endroid QR Code.
$result = Builder::create()
->writer(new PngWriter())
->data($otpauthUrl)
->build();
header('Content-Type: ' . $result->getMimeType());
echo $result->getString();
?>