diff --git a/include/lucidAuth.functions.php b/include/lucidAuth.functions.php index e9d9b3a..ba80a30 100644 --- a/include/lucidAuth.functions.php +++ b/include/lucidAuth.functions.php @@ -57,19 +57,41 @@ function authenticateLDAP (string $username, string $password) { } } -function storeToken (string $username, string $password, object $cookie) { - global $settings; +function storeToken (string $secureToken, string $qualifiedUsername, string $httpHost) { + global $settings, $pdoDB; -} - -function retrieveTokenFromDB (string $username, string $foo) { - global $settings; + // Save authentication token in database serverside + try { + $pdoQuery = $pdoDB->prepare(' + INSERT INTO SecureToken (UserId, Value) + SELECT User.Id, :securetoken + FROM User + WHERE User.Username = :qualifiedusername + '); + $pdoQuery->execute([ + ':securetoken' => $secureToken, + ':qualifiedusername' => $qualifiedUsername + ]); + } + catch (Exception $e) { + return ['status' => 'Fail', 'reason' => $e]; + } + // Save authentication token in cookie clientside + $cookieDomain = array_values(array_filter($settings->Session['CookieDomains'], function ($value) use ($httpHost) { + // Check if $_SERVER['HTTP_HOST'] matches any of the configured domains (either explicitly or as a subdomain) + // This might seem backwards, but relying on $_SERVER directly allows spoofed values with potential security risks + return (strlen($value) > strlen($httpHost)) ? false : (0 === substr_compare($httpHost, $value, -strlen($value))); + }))[0]; + if (setcookie('JWT', $secureToken, (time() + $settings->Session['Duration']), '/', '.' . $cookieDomain)) { + return ['status' => 'Success']; + } } function validateToken (string $secureToken) { global $settings, $pdoDB; + // Decode provided authentication token try { $jwtPayload = JWT::decode($secureToken, base64_decode($settings->JWT['PrivateKey_base64']), $settings->JWT['Algorithm']); } catch (Exception $e) { @@ -82,6 +104,7 @@ function validateToken (string $secureToken) { return ['status' => 'Fail', 'reason' => '3']; } + // Retrieve all authentication tokens from database matching username $pdoQuery = $pdoDB->prepare(' SELECT SecureToken.Value FROM SecureToken @@ -100,13 +123,12 @@ function validateToken (string $secureToken) { } } + // Compare provided authentication token to all stored tokens in database if (!empty($storedTokens) && sizeof(array_filter($storedTokens, function ($value) use ($jwtPayload) { return $value->iat === $jwtPayload->iat; })) === 1) { - // At least one of the database-stored tokens match - return ['status' => 'Success', 'token' => $jwtPayload]; + return ['status' => 'Success']; } else { - // No matching token in database return ['status' => 'Fail', 'reason' => '2']; } } diff --git a/public/lucidAuth.login.php b/public/lucidAuth.login.php index f51d157..c663f74 100644 --- a/public/lucidAuth.login.php +++ b/public/lucidAuth.login.php @@ -6,23 +6,12 @@ if ($_POST['do'] === 'login') { $result = authenticateLDAP($_POST['username'], $_POST['password']); if ($result['status'] === 'Success') { - // Save authentication token in database - $pdoQuery = $pdoDB->prepare(' - INSERT INTO SecureToken (UserId, Value) - SELECT User.Id, :securetoken - FROM User - WHERE User.Username = :qualifiedusername - '); - $pdoQuery->execute([ - ':securetoken' => $result['token'], - ':qualifiedusername' => $settings->LDAP['Domain'] . '\\' . $_POST['username'] - ]); - // Save authentication token in cookie - $httpHost = $_SERVER['HTTP_HOST']; - $cookieDomain = array_values(array_filter($settings->Session['CookieDomains'], function ($value) use ($httpHost) { - return (strlen($value) > strlen($httpHost)) ? false : (0 === substr_compare($httpHost, $value, -strlen($value))); - }))[0]; - setcookie('JWT', $result['token'], (time() + $settings->Session['Duration']), '/', '.' . $cookieDomain); + // Store authentication token; in database serverside & in cookie clientside + if (storeToken($result['token'], $settings->LDAP['Domain'] . '\\' . $_POST['username'], $_SERVER['HTTP_HOST'])['status'] !== 'Success') { + // Since this action is only ever called through an AJAX-request; return JSON object + echo '{"Result":"Fail","Reason":"Failed storing authentication token in database and/or cookie"}' . PHP_EOL; + exit; + } // Convert base64 encoded string back from JSON; // forcing it into an associative array (instead of javascript's default StdClass object) @@ -30,13 +19,13 @@ $proxyHeaders = json_decode(base64_decode($_POST['ref']), JSON_OBJECT_AS_ARRAY); } catch (Exception $e) { - // Since this request is only ever called through an AJAX-request; return JSON object + // Since this action is only ever called through an AJAX-request; return JSON object echo '{"Result":"Fail","Reason":"Original request URI lost in transition"}' . PHP_EOL; exit; } $originalUri = !empty($proxyHeaders) ? $proxyHeaders['XForwardedProto'] . '://' . $proxyHeaders['XForwardedHost'] . $proxyHeaders['XForwardedUri'] : 'lucidAuth.manage.php'; - // Since this request is only ever called through an AJAX-request; return JSON object + // Since this action is only ever called through an AJAX-request; return JSON object echo '{"Result":"Success","Location":"' . $originalUri . '"}' . PHP_EOL; } else { switch ($result['reason']) { diff --git a/public/lucidAuth.validateRequest.php b/public/lucidAuth.validateRequest.php index 41ac22e..ee300df 100644 --- a/public/lucidAuth.validateRequest.php +++ b/public/lucidAuth.validateRequest.php @@ -20,7 +20,7 @@ file_put_contents('../requestHeaders.log', (new DateTime())->format('Y-m-d\TH:i:s.u') . ' --- ' . (json_encode($proxyHeaders, JSON_FORCE_OBJECT)) . PHP_EOL, FILE_APPEND); } - if (sizeof($proxyHeaders) == 0) { + if (sizeof($proxyHeaders) === 0) { // Non-proxied request; this is senseless, go fetch! header("HTTP/1.1 403 Forbidden"); exit;