Skip to content

Commit

Permalink
Merge branch 'feature/add-auth-to-ws-20241025' into development
Browse files Browse the repository at this point in the history
  • Loading branch information
webpwnized committed Nov 10, 2024
2 parents 981fea7 + 46490ef commit 8b98a5e
Showing 1 changed file with 73 additions and 41 deletions.
114 changes: 73 additions & 41 deletions src/webservices/rest/ws-login.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,31 @@
require_once '../../classes/JWT.php';
require_once '../../classes/SQLQueryHandler.php';

// Configuration Constants
define('JWT_SECRET_KEY', getenv('JWT_SECRET_KEY') ?: 'snowman');
define('JWT_EXPIRATION_TIME', 3600); // Token expiration time in seconds
define('MAX_FAILED_ATTEMPTS', 5); // Maximum number of failed login attempts
define('CORS_MAX_AGE', 600); // CORS preflight cache duration in seconds

define('TRUSTED_ORIGINS', [
'http://mutillidae.localhost'
]);

// Define the Base URL dynamically based on the current request
define('BASE_URL', ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST']);

// Initialize SQLQueryHandler
$lSQLQueryHandler = new SQLQueryHandler(0);

// Get the origin of the request
$lOrigin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : 'https://mutillidae.localhost';
$lOrigin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : BASE_URL;

// Get the current domain dynamically
$lCurrentDomain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'mutillidae.localhost';
$lScheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$lBaseUrl = "{$lScheme}://{$lCurrentDomain}";
// CORS Validation - Only allow trusted origins
if (!in_array($lOrigin, TRUSTED_ORIGINS)) {
http_response_code(403);
echo json_encode(["error" => "Origin not allowed."]);
exit();
}

// Set CORS headers
header("Access-Control-Allow-Origin: $lOrigin");
Expand All @@ -24,7 +39,7 @@
// Handle preflight requests
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header('Access-Control-Allow-Origin: ' . $lOrigin);
header('Access-Control-Max-Age: 600');
header('Access-Control-Max-Age: ' . CORS_MAX_AGE);
http_response_code(204);
exit();
}
Expand All @@ -42,44 +57,36 @@
$lClientSecret = $lData['client_secret'] ?? null;
$lAudience = $lData['audience'] ?? null;

if (!$lClientId) {
// Validate Inputs
if (!isset($lClientId) || !preg_match('/^[a-f0-9]{32}$/', $lClientId)) {
http_response_code(400);
echo json_encode(["error" => "Client ID is missing."]);
echo json_encode(["error" => "Invalid Client ID format."]);
exit();
}

if (!$lClientSecret) {
if (!isset($lClientSecret) || !preg_match('/^[a-f0-9]{64}$/', $lClientSecret)) {
http_response_code(400);
echo json_encode(["error" => "Client secret is missing."]);
echo json_encode(["error" => "Invalid Client Secret format."]);
exit();
}

if (!$lAudience) {
if (!isset($lAudience) || !filter_var($lAudience, FILTER_VALIDATE_URL)) {
http_response_code(400);
echo json_encode(["error" => "Audience is missing."]);
exit();
}

// Validate credentials
$lIsValid = $lSQLQueryHandler->authenticateByClientCredentials($lClientId, $lClientSecret);
if (!$lIsValid) {
http_response_code(401);
echo json_encode(["error" => "Invalid client credentials."]);
echo json_encode(["error" => "Invalid Audience format."]);
exit();
}

// Define a list of valid audiences based on known endpoints
$lValidAudiences = [
"$lBaseUrl/rest/ws-cors-echo.php",
"$lBaseUrl/rest/ws-dns-lookup.php",
"$lBaseUrl/rest/ws-echo.php",
"$lBaseUrl/rest/ws-prototype-login.php",
"$lBaseUrl/rest/ws-test-connectivity.php",
"$lBaseUrl/rest/ws-user-account.php",
"$lBaseUrl/soap/ws-dns-lookup.php",
"$lBaseUrl/soap/ws-echo.php",
"$lBaseUrl/soap/ws-test-connectivity.php",
"$lBaseUrl/soap/ws-user-account.php"
BASE_URL . "/rest/ws-cors-echo.php",
BASE_URL . "/rest/ws-dns-lookup.php",
BASE_URL . "/rest/ws-echo.php",
BASE_URL . "/rest/ws-test-connectivity.php",
BASE_URL . "/rest/ws-user-account.php",
BASE_URL . "/soap/ws-dns-lookup.php",
BASE_URL . "/soap/ws-echo.php",
BASE_URL . "/soap/ws-test-connectivity.php",
BASE_URL . "/soap/ws-user-account.php"
];

// Check if the requested audience is valid
Expand All @@ -89,27 +96,52 @@
exit();
}

// Rate limiting mechanism
session_start();
$lFailedAttemptsKey = "failed_attempts_" . $_SERVER['REMOTE_ADDR'];
if (!isset($_SESSION[$lFailedAttemptsKey])) {
$_SESSION[$lFailedAttemptsKey] = 0;
}

// Lockout mechanism after MAX_FAILED_ATTEMPTS failed attempts
if ($_SESSION[$lFailedAttemptsKey] >= MAX_FAILED_ATTEMPTS) {
http_response_code(429); // Too Many Requests
echo json_encode(["error" => "Too many failed attempts. Please try again later."]);
exit();
}

// Validate credentials
$lIsValid = $lSQLQueryHandler->authenticateByClientCredentials($lClientId, $lClientSecret);
if (!$lIsValid) {
$_SESSION[$lFailedAttemptsKey]++;
http_response_code(401);
echo json_encode(["error" => "Authentication failed."]);
exit();
} else {
// Reset failed attempts on successful login
$_SESSION[$lFailedAttemptsKey] = 0;
}

// Define JWT claims with audience
$lPayload = [
'iss' => $lBaseUrl, // Issuer is your domain
'aud' => $lAudience, // Include audience in token
'iat' => time(),
'nbf' => time(),
'exp' => time() + 3600,
'sub' => $lClientId,
'jti' => bin2hex(random_bytes(16))
'iss' => BASE_URL, // Issuer is your domain
'aud' => $lAudience, // Audience for the token
'iat' => time(), // Issued at
'nbf' => time(), // Not before
'exp' => time() + JWT_EXPIRATION_TIME, // Expiration time
'sub' => $lClientId, // Subject is the client ID
'jti' => bin2hex(random_bytes(16)) // JWT ID
];

// Encode the JWT token
$lSecretKey = getenv('JWT_SECRET_KEY') ?: 'snowman';
$lJwt = JWT::encode($lPayload, $lSecretKey);
// Encode the JWT token with a specified algorithm
$lJwt = JWT::encode($lPayload, JWT_SECRET_KEY, 'HS256'); // Use a secure algorithm

// Respond with JWT token
http_response_code(200);
echo json_encode([
'access_token' => $lJwt,
'token_type' => 'bearer',
'expires_in' => 3600
'expires_in' => JWT_EXPIRATION_TIME
]);

?>

0 comments on commit 8b98a5e

Please sign in to comment.