execute($sql)) { return $code; } return false; } /** * Exchange authorization code for access token * @param string $code Authorization code * @param string $client_id Client ID * @param string $client_secret Client secret * @return array|false Token data or false */ public static function exchangeCode($code, $client_id, $client_secret) { self::init(); $code_safe = VDatabase::escape($code); $client_id_safe = VDatabase::escape($client_id); // Validate code $sql = "SELECT * FROM db_oauth_codes WHERE code = '$code_safe' AND client_id = '$client_id_safe' AND expires_at > NOW() AND is_used = 0"; $result = self::$db->execute($sql); if (!$result || $result->RecordCount() == 0) { return false; } $code_data = $result->FetchRow(); // Mark code as used $code_id = (int)$code_data['code_id']; self::$db->execute("UPDATE db_oauth_codes SET is_used = 1 WHERE code_id = $code_id"); // Generate tokens $access_token = bin2hex(random_bytes(32)); $refresh_token = bin2hex(random_bytes(32)); $usr_id = (int)$code_data['usr_id']; $scopes = $code_data['scopes']; $sql = "INSERT INTO db_oauth_tokens (usr_id, client_id, access_token, refresh_token, token_type, scopes, expires_at, refresh_expires_at, created_at) VALUES ($usr_id, '$client_id_safe', '$access_token', '$refresh_token', 'Bearer', '$scopes', DATE_ADD(NOW(), INTERVAL 1 HOUR), DATE_ADD(NOW(), INTERVAL 30 DAY), NOW())"; if (self::$db->execute($sql)) { return [ 'access_token' => $access_token, 'refresh_token' => $refresh_token, 'token_type' => 'Bearer', 'expires_in' => 3600, 'scope' => implode(' ', json_decode($scopes, true)) ]; } return false; } /** * Refresh access token * @param string $refresh_token Refresh token * @param string $client_id Client ID * @return array|false New token data or false */ public static function refreshToken($refresh_token, $client_id) { self::init(); $refresh_safe = VDatabase::escape($refresh_token); $client_id_safe = VDatabase::escape($client_id); // Validate refresh token $sql = "SELECT * FROM db_oauth_tokens WHERE refresh_token = '$refresh_safe' AND client_id = '$client_id_safe' AND refresh_expires_at > NOW() AND is_revoked = 0"; $result = self::$db->execute($sql); if (!$result || $result->RecordCount() == 0) { return false; } $token_data = $result->FetchRow(); // Revoke old token $token_id = (int)$token_data['token_id']; self::$db->execute("UPDATE db_oauth_tokens SET is_revoked = 1 WHERE token_id = $token_id"); // Generate new tokens $new_access_token = bin2hex(random_bytes(32)); $new_refresh_token = bin2hex(random_bytes(32)); $usr_id = (int)$token_data['usr_id']; $scopes = $token_data['scopes']; $sql = "INSERT INTO db_oauth_tokens (usr_id, client_id, access_token, refresh_token, token_type, scopes, expires_at, refresh_expires_at, created_at) VALUES ($usr_id, '$client_id_safe', '$new_access_token', '$new_refresh_token', 'Bearer', '$scopes', DATE_ADD(NOW(), INTERVAL 1 HOUR), DATE_ADD(NOW(), INTERVAL 30 DAY), NOW())"; if (self::$db->execute($sql)) { return [ 'access_token' => $new_access_token, 'refresh_token' => $new_refresh_token, 'token_type' => 'Bearer', 'expires_in' => 3600 ]; } return false; } /** * Validate access token * @param string $access_token Access token * @return array|false User and scope data or false */ public static function validateToken($access_token) { self::init(); $token_safe = VDatabase::escape($access_token); $sql = "SELECT t.*, u.usr_user, u.usr_email FROM db_oauth_tokens t JOIN db_accountuser u ON t.usr_id = u.usr_id WHERE t.access_token = '$token_safe' AND t.expires_at > NOW() AND t.is_revoked = 0"; $result = self::$db->execute($sql); if ($result && $result->RecordCount() > 0) { $data = $result->FetchRow(); return [ 'usr_id' => $data['usr_id'], 'username' => $data['usr_user'], 'email' => $data['usr_email'], 'scopes' => json_decode($data['scopes'], true) ]; } return false; } /** * Revoke token * @param string $access_token Access token * @return bool Success */ public static function revokeToken($access_token) { self::init(); $token_safe = VDatabase::escape($access_token); $sql = "UPDATE db_oauth_tokens SET is_revoked = 1 WHERE access_token = '$token_safe'"; return self::$db->execute($sql); } }