Files
FileRise/totp_saveCode.php
2025-04-11 22:36:43 -04:00

85 lines
2.6 KiB
PHP

<?php
// totp_saveCode.php
require_once 'config.php';
header('Content-Type: application/json');
// 1) Only allow POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
error_log("totp_saveCode: invalid method {$_SERVER['REQUEST_METHOD']}");
exit(json_encode(['status'=>'error','message'=>'Method not allowed']));
}
// 2) CSRF check
$headers = array_change_key_case(getallheaders(), CASE_LOWER);
$csrfHeader = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : '';
if (!isset($_SESSION['csrf_token']) || $csrfHeader !== $_SESSION['csrf_token']) {
respond('error', 403, 'Invalid CSRF token');
}
// 3) Must be logged in
if (empty($_SESSION['username'])) {
http_response_code(401);
error_log("totp_saveCode: unauthorized attempt from IP {$_SERVER['REMOTE_ADDR']}");
exit(json_encode(['status'=>'error','message'=>'Unauthorized']));
}
// 4) Validate username format
$userId = $_SESSION['username'];
if (!preg_match(REGEX_USER, $userId)) {
http_response_code(400);
error_log("totp_saveCode: invalid username format: {$userId}");
exit(json_encode(['status'=>'error','message'=>'Invalid user identifier']));
}
// 5) Ensure user file exists (create if missing)
$userFile = rtrim(USERS_DIR, '/\\') . DIRECTORY_SEPARATOR . $userId . '.json';
if (!file_exists($userFile)) {
$defaultData = [];
if (file_put_contents($userFile, json_encode($defaultData)) === false) {
http_response_code(500);
error_log("totp_saveCode: failed to create user file: {$userFile}");
exit(json_encode(['status'=>'error','message'=>'Server error']));
}
}
// 6) Generate secure recovery code
function generateRecoveryCode($length = 12) {
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$max = strlen($chars) - 1;
$code = '';
for ($i = 0; $i < $length; $i++) {
$code .= $chars[random_int(0, $max)];
}
return $code;
}
$recoveryCode = generateRecoveryCode();
$recoveryHash = password_hash($recoveryCode, PASSWORD_DEFAULT);
// 7) Read, lock, update user file
$fp = fopen($userFile, 'c+');
if (!$fp || !flock($fp, LOCK_EX)) {
http_response_code(500);
error_log("totp_saveCode: failed to lock user file: {$userFile}");
exit(json_encode(['status'=>'error','message'=>'Server error']));
}
$data = json_decode(stream_get_contents($fp), true) ?: [];
$data['totp_recovery_code'] = $recoveryHash;
rewind($fp);
ftruncate($fp, 0);
fwrite($fp, json_encode($data)); // no pretty-print in prod
fflush($fp);
flock($fp, LOCK_UN);
fclose($fp);
// 8) Return one-time recovery code
echo json_encode([
'status' => 'ok',
'recoveryCode' => $recoveryCode
]);
exit;