Sync current dev state
Some checks failed
EasyStream Test Suite / test (pull_request) Has been cancelled
EasyStream Test Suite / code-quality (pull_request) Has been cancelled
EasyStream Test Suite / integration-test (pull_request) Has been cancelled

This commit is contained in:
SamiAhmed7777
2025-12-15 17:28:21 -08:00
parent 3bf64b1058
commit f0f346deb9
54 changed files with 11060 additions and 484 deletions

View File

@@ -750,4 +750,211 @@ class VAuth
'token' => substr($token, 0, 8) . '...' // Log partial token for debugging
]);
}
/**
* Generate JWT token for API authentication
* @param array $user User data
* @param int|null $expiryTime Optional custom expiry time in seconds
* @return string JWT token
*/
public function generateJWTToken($user, $expiryTime = null)
{
try {
$expiryTime = $expiryTime ?? (24 * 60 * 60); // Default 24 hours
$header = json_encode([
'typ' => 'JWT',
'alg' => 'HS256'
]);
$payload = json_encode([
'user_id' => $user['user_id'],
'username' => $user['username'],
'email' => $user['email'],
'role' => $user['role'] ?? 'member',
'iat' => time(),
'exp' => time() + $expiryTime
]);
$headerEncoded = $this->base64UrlEncode($header);
$payloadEncoded = $this->base64UrlEncode($payload);
$jwtSecret = getenv('JWT_SECRET') ?: (defined('JWT_SECRET') ? JWT_SECRET : 'change_this_jwt_secret');
$signature = hash_hmac('sha256', $headerEncoded . '.' . $payloadEncoded, $jwtSecret, true);
$signatureEncoded = $this->base64UrlEncode($signature);
return $headerEncoded . '.' . $payloadEncoded . '.' . $signatureEncoded;
} catch (Exception $e) {
$this->logger->error('JWT generation failed', [
'error' => $e->getMessage(),
'user_id' => $user['user_id'] ?? 'unknown'
]);
throw $e;
}
}
/**
* Validate JWT token
* @param string $token JWT token
* @return array|null User data or null if invalid
*/
public function validateJWTToken($token)
{
try {
$parts = explode('.', $token);
if (count($parts) !== 3) {
$this->logger->logSecurityEvent('Invalid JWT format', ['token' => substr($token, 0, 20) . '...']);
return null;
}
list($headerEncoded, $payloadEncoded, $signatureProvided) = $parts;
// Verify signature
$jwtSecret = getenv('JWT_SECRET') ?: (defined('JWT_SECRET') ? JWT_SECRET : 'change_this_jwt_secret');
$expectedSignature = hash_hmac('sha256', $headerEncoded . '.' . $payloadEncoded, $jwtSecret, true);
$expectedSignatureEncoded = $this->base64UrlEncode($expectedSignature);
if (!hash_equals($expectedSignatureEncoded, $signatureProvided)) {
$this->logger->logSecurityEvent('JWT signature verification failed', []);
return null;
}
// Decode payload
$payload = json_decode($this->base64UrlDecode($payloadEncoded), true);
if (!$payload || !isset($payload['user_id'])) {
$this->logger->logSecurityEvent('Invalid JWT payload', []);
return null;
}
// Check expiration
if (isset($payload['exp']) && $payload['exp'] < time()) {
$this->logger->logSecurityEvent('JWT token expired', ['user_id' => $payload['user_id']]);
return null;
}
// Verify user exists and is active
$sql = "SELECT `user_id`, `username`, `email`, `role`, `status`
FROM `db_users`
WHERE `user_id` = ? AND `status` = 'active'";
$result = $this->db->dbConnection()->Execute($sql, [$payload['user_id']]);
if (!$result || $result->EOF) {
$this->logger->logSecurityEvent('JWT user not found or inactive', ['user_id' => $payload['user_id']]);
return null;
}
$user = $result->fields;
return [
'user_id' => $user['user_id'],
'username' => $user['username'],
'email' => $user['email'],
'role' => $user['role']
];
} catch (Exception $e) {
$this->logger->error('JWT validation error', [
'error' => $e->getMessage(),
'token' => substr($token, 0, 20) . '...'
]);
return null;
}
}
/**
* Login with JWT token return (for API clients)
* @param string $identifier Username or email
* @param string $password Password
* @param int|null $expiryTime Optional token expiry time
* @return array Result with token and user data
*/
public function loginWithToken($identifier, $password, $expiryTime = null)
{
try {
// Use regular login to validate credentials
$loginResult = $this->login($identifier, $password, false);
if (!$loginResult['success']) {
return $loginResult;
}
// Generate JWT token
$token = $this->generateJWTToken($loginResult['user'], $expiryTime);
return [
'success' => true,
'message' => 'Login successful',
'token' => $token,
'token_type' => 'Bearer',
'expires_in' => $expiryTime ?? (24 * 60 * 60),
'user' => $loginResult['user']
];
} catch (Exception $e) {
$this->logger->error('Token login error', [
'error' => $e->getMessage(),
'identifier' => $identifier ?? 'unknown'
]);
return ['success' => false, 'message' => 'An error occurred during login'];
}
}
/**
* Authenticate request via Bearer token (for API requests)
* @param string|null $authHeader Authorization header value
* @return array|null User data or null if not authenticated
*/
public function authenticateBearer($authHeader = null)
{
try {
// Get Authorization header if not provided
if ($authHeader === null) {
$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? '';
// Apache mod_rewrite workaround
if (empty($authHeader) && function_exists('apache_request_headers')) {
$headers = apache_request_headers();
$authHeader = $headers['Authorization'] ?? $headers['authorization'] ?? '';
}
}
if (empty($authHeader)) {
return null;
}
// Extract Bearer token
if (strpos($authHeader, 'Bearer ') === 0) {
$token = substr($authHeader, 7);
return $this->validateJWTToken($token);
}
return null;
} catch (Exception $e) {
$this->logger->error('Bearer authentication error', ['error' => $e->getMessage()]);
return null;
}
}
/**
* Base64 URL-safe encoding
* @param string $data Data to encode
* @return string Encoded data
*/
private function base64UrlEncode($data)
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
/**
* Base64 URL-safe decoding
* @param string $data Data to decode
* @return string Decoded data
*/
private function base64UrlDecode($data)
{
return base64_decode(strtr($data, '-_', '+/'));
}
}