diff --git a/data/web/inc/lib/Yubico.php b/data/web/inc/lib/Yubico.php
index d8aa41c3..cde9f4d1 100644
--- a/data/web/inc/lib/Yubico.php
+++ b/data/web/inc/lib/Yubico.php
@@ -5,7 +5,7 @@
    * @category    Auth
    * @package     Auth_Yubico
    * @author      Simon Josefsson <simon@yubico.com>, Olov Danielson <olov@yubico.com>
-   * @copyright   2007-2015 Yubico AB
+   * @copyright   2007-2020 Yubico AB
    * @license     https://opensource.org/licenses/bsd-license.php New BSD License
    * @version     2.0
    * @link        https://www.yubico.com/
@@ -50,12 +50,6 @@ class Auth_Yubico
 	 */
 	var $_key;
 
-	/**
-	 * URL part of validation server
-	 * @var string
-	 */
-	var $_url;
-
 	/**
 	 * List with URL part of validation servers
 	 * @var array
@@ -80,12 +74,24 @@ class Auth_Yubico
 	 */
 	var $_response;
 
+	/**
+	 * Number of times we retried in our last validation
+	 * @var int
+	 */
+	var $_retries;
+
 	/**
 	 * Flag whether to verify HTTPS server certificates or not.
 	 * @var boolean
 	 */
 	var $_httpsverify;
 
+	/**
+	 * Maximum number of times we will retry transient HTTP errors
+	 * @var int
+	 */
+	var $_max_retries;
+
 	/**
 	 * Constructor
 	 *
@@ -98,39 +104,37 @@ class Auth_Yubico
 	 *                                 default true)
 	 * @access public
 	 */
-	public function __construct($id, $key = '', $https = 0, $httpsverify = 1)
+        public function __construct($id, $key = '', $https = 0, $httpsverify = 1, $max_retries = 3)
 	{
 		$this->_id =  $id;
 		$this->_key = base64_decode($key);
 		$this->_httpsverify = $httpsverify;
+		$this->_max_retries = $max_retries;
 	}
 
 	/**
 	 * Specify to use a different URL part for verification.
-	 * The default is "api.yubico.com/wsapi/verify".
+	 * The default is "https://api.yubico.com/wsapi/2.0/verify".
 	 *
 	 * @param  string $url  New server URL part to use
 	 * @access public
+	 * @deprecated
 	 */
 	function setURLpart($url)
 	{
-		$this->_url = $url;
+	  $this->_url_list = array($url);
 	}
 
 	/**
 	 * Get next URL part from list to use for validation.
 	 *
-	 * @return mixed string with URL part of false if no more URLs in list
+	 * @return mixed string with URL part or false if no more URLs in list
 	 * @access public
 	 */
 	function getNextURLpart()
 	{
 	  if ($this->_url_list) $url_list=$this->_url_list;
-	  else $url_list=array('https://api.yubico.com/wsapi/2.0/verify',
-			       'https://api2.yubico.com/wsapi/2.0/verify',
-			       'https://api3.yubico.com/wsapi/2.0/verify',
-			       'https://api4.yubico.com/wsapi/2.0/verify',
-			       'https://api5.yubico.com/wsapi/2.0/verify');
+	  else $url_list=array('https://api.yubico.com/wsapi/2.0/verify');
 
 	  if ($this->_url_index>=count($url_list)) return false;
 	  else return $url_list[$this->_url_index++];
@@ -178,6 +182,17 @@ class Auth_Yubico
 		return $this->_response;
 	}
 
+	/**
+	 * Return the number of retries that were used in the last validation
+	 *
+	 * @return int     Number of retries
+	 * @access public
+	 */
+	function getRetries()
+	{
+		return $this->_retries;
+	}
+
 	/**
 	 * Parse input string into password, yubikey prefix,
 	 * ciphertext, and OTP.
@@ -238,6 +253,26 @@ class Auth_Yubico
 	  return $param_array;
 	}
 
+        function _make_curl_handle($query, $timeout=null)
+	{
+	    flush();
+	    $handle = curl_init($query);
+	    curl_setopt($handle, CURLOPT_USERAGENT, "PEAR Auth_Yubico");
+	    curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
+	    if (!$this->_httpsverify) {
+		curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, 0);
+		curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, 0);
+	    }
+	    curl_setopt($handle, CURLOPT_FAILONERROR, true);
+	    /* If timeout is set, we better apply it here as well
+	     * in case the validation server fails to follow it. */
+	    if ($timeout) {
+		curl_setopt($handle, CURLOPT_TIMEOUT, $timeout);
+	    }
+
+	    return $handle;
+	}
+
 	/**
 	 * Verify Yubico OTP against multiple URLs
 	 * Protocol specification 2.0 is used to construct validation requests
@@ -252,12 +287,19 @@ class Auth_Yubico
 	 *                             and 100 or "fast" or "secure".
 	 * @param int $timeout         Max number of seconds to wait
 	 *                             for responses
+	 * @param int $max_retries     Max number of times we will retry on
+	 *                             transient errors.
 	 * @return mixed               PEAR error on error, true otherwise
 	 * @access public
 	 */
 	function verify($token, $use_timestamp=null, $wait_for_all=False,
-			$sl=null, $timeout=null)
+			$sl=null, $timeout=null, $max_retries=null)
 	{
+	  /* If maximum retries is not set, default from instance */
+	  if (is_null($max_retries)) {
+	    $max_retries = $this->_max_retries;
+	  }
+
 	  /* Construct parameters string */
 	  $ret = $this->parsePasswordOTP($token);
 	  if (!$ret) {
@@ -284,10 +326,13 @@ class Auth_Yubico
 	  }
 
 	  /* Generate and prepare request. */
-	  $this->_lastquery=null;
+	  $this->_lastquery = null;
+	  $this->_retries = 0;
 	  $this->URLreset();
+
 	  $mh = curl_multi_init();
 	  $ch = array();
+	  $retries = array();
 	  while($URLpart=$this->getNextURLpart()) 
 	    {
 	      $query = $URLpart . "?" . $parameters;
@@ -295,21 +340,11 @@ class Auth_Yubico
 	      if ($this->_lastquery) { $this->_lastquery .= " "; }
 	      $this->_lastquery .= $query;
 	      
-	      $handle = curl_init($query);
-	      curl_setopt($handle, CURLOPT_USERAGENT, "PEAR Auth_Yubico");
-	      curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
-	      if (!$this->_httpsverify) {
-		curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, 0);
-		curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, 0);
-	      }
-	      curl_setopt($handle, CURLOPT_FAILONERROR, true);
-	      /* If timeout is set, we better apply it here as well
-	         in case the validation server fails to follow it. 
-	      */ 
-	      if ($timeout) curl_setopt($handle, CURLOPT_TIMEOUT, $timeout);
+	      $handle = $this->_make_curl_handle($query, $timeout);
 	      curl_multi_add_handle($mh, $handle);
 	      
 	      $ch[(int)$handle] = $handle;
+	      $retries[$query] = 0;
 	    }
 
 	  /* Execute and read request. */
@@ -319,19 +354,20 @@ class Auth_Yubico
 	  do {
 	    /* Let curl do its work. */
 	    while (($mrc = curl_multi_exec($mh, $active))
-		   == CURLM_CALL_MULTI_PERFORM)
-	      ;
+		   == CURLM_CALL_MULTI_PERFORM) {
+	      curl_multi_select($mh);
+	    }
 
 	    while ($info = curl_multi_info_read($mh)) {
+	      $cinfo = curl_getinfo ($info['handle']);
 	      if ($info['result'] == CURLE_OK) {
-
 		/* We have a complete response from one server. */
 
 		$str = curl_multi_getcontent($info['handle']);
-		$cinfo = curl_getinfo ($info['handle']);
 		
 		if ($wait_for_all) { # Better debug info
-		  $this->_response .= 'URL=' . $cinfo['url'] ."\n"
+		  $this->_response .= 'URL=' . $cinfo['url'] . ' HTTP_CODE='
+		    . $cinfo['http_code'] . "\n"
 		    . $str . "\n";
 		}
 
@@ -401,7 +437,7 @@ class Auth_Yubico
 		    }
 		  }
 		}
-		if (!$wait_for_all && ($valid || $replay)) 
+		if (!$wait_for_all && ($valid || $replay))
 		  {
 		    /* We have status=OK or status=REPLAYED_OTP, return. */
 		    foreach ($ch as $h) {
@@ -413,12 +449,35 @@ class Auth_Yubico
 		    if ($valid) return true;
 		    return PEAR::raiseError($status);
 		  }
-		
-		curl_multi_remove_handle($mh, $info['handle']);
-		curl_close($info['handle']);
-		unset ($ch[(int)$info['handle']]);
+	      } else {
+		/* Some kind of error, but def. not a 200 response */
+		/* No status= in response body */
+		$http_status_code = (int)$cinfo['http_code'];
+		$query = $cinfo['url'];
+		if ($http_status_code == 400 ||
+		    ($http_status_code >= 500 && $http_status_code < 600)) {
+		  /* maybe retry */
+		  if ($retries[$query] < $max_retries) {
+		    $retries[$query]++;  // for this server
+		    $this->_retries++;   // for this validation attempt
+
+		    $newhandle = $this->_make_curl_handle($query, $timeout);
+
+		    curl_multi_add_handle($mh, $newhandle);
+		    $ch[(int)$newhandle] = $newhandle;
+
+		    // Loop back up to curl_multi_exec, even if this
+		    // was the last handle and curl_multi_exec _was_
+		    // no longer active, it's active again now we've
+		    // added a retry.
+		    $active = true;
+		  }
+		}
 	      }
-	      curl_multi_select($mh);
+	      /* Done with this handle */
+	      curl_multi_remove_handle($mh, $info['handle']);
+	      curl_close($info['handle']);
+	      unset ($ch[(int)$info['handle']]);
 	    }
 	  } while ($active);