diff --git a/auth/shibboleth/auth.php b/auth/shibboleth/auth.php
index fe6244c8..fc8457a3 100644
--- a/auth/shibboleth/auth.php
+++ b/auth/shibboleth/auth.php
@@ -62,7 +62,7 @@ class auth_plugin_shibboleth extends auth_plugin_base {
      * @return bool Authentication success or failure.
      */
     function user_login($username, $password) {
-       global $SESSION;
+       global $SESSION, $CFG;
 
         // If we are in the shibboleth directory then we trust the server var
         if (!empty($_SERVER[$this->config->user_attribute])) {
@@ -84,6 +84,162 @@ class auth_plugin_shibboleth extends auth_plugin_base {
             $SESSION->shibboleth_session_id  = $sessionkey;
 
             return (strtolower($_SERVER[$this->config->user_attribute]) == strtolower($username));
+        } else if (!empty($this->config->auth_background) && optional_param('service', '', PARAM_ALPHANUMEXT)) {
+            // We need to try to authenticate to the IdP. This may not end well, but let's try.
+            // 1. Visit our login page to redirect to the IdP login page.
+            require_once($CFG->libdir . '/filelib.php');
+
+            $cookiefile = $CFG->dataroot . '/' . uniqid('shibcurlcookie', true) . '.txt';
+
+            $curl = new curl(array('cookie' => $cookiefile));
+            $url = new moodle_url('/auth/shibboleth/index.php'); // This should be Shibbolized already.
+            $options = array(
+                'FRESH_CONNECT'     => true,
+                'RETURNTRANSFER'    => true,
+                'FORBID_REUSE'      => true,
+                'HEADER'            => 0,
+                'CONNECTTIMEOUT'    => 3,
+                'FOLLOWLOCATION'    => true,
+            );
+            $loginpage = $curl->get($url, array(), $options);
+            $loginpageinfo = $curl->get_info();
+
+            if (empty($loginpage)) {
+                error_log('[client ' . getremoteaddr() . "]  $CFG->wwwroot  Failed background login from Shibboleth");
+                @unlink($cookiefile);
+                return false; // Nothing back?
+            }
+
+            // 2. Try to find the login form.
+            $dom = new DOMDocument();
+            if (!$dom->loadHTML($loginpage)) {
+                error_log('[client ' . getremoteaddr() . "]  $CFG->wwwroot  Invalid background login page from Shibboleth");
+                @unlink($cookiefile);
+                return false;
+            }
+
+            // This is super icky. We're looking essentially for a form that has a text and a password element.
+            // And grabbing all other elements as we go.
+            $validform = false;
+            foreach ($dom->getElementsByTagName('form') as $form) {
+                $formvalues  = array();
+                $haspassword = false;
+                $hasinput    = false;
+                $formurl     = $form->getAttribute('action');
+
+                // Fetch buttons first.
+                foreach ($form->getElementsByTagName('button') as $button) {
+                    if ($button->getAttribute('name')) {
+                        $formvalues[$button->getAttribute('name')] = $button->getAttribute('value');
+                    }
+                }
+
+                // Fetch inputs next, and try to sift out what's going on with them.
+                foreach ($form->getElementsByTagName('input') as $input) {
+                    switch ($input->getAttribute('type')) {
+                        case 'password':
+                            // So we have a password field. Did we already have one?
+                            if (empty($haspassword)) {
+                                $haspassword = $input->getAttribute('name');
+                            } else {
+                                // This isn't it. Skip to the next possible form.
+                                $haspassword = false;
+                                error_log('[client ' . getremoteaddr() . "]  $CFG->wwwroot  Form has two password items");
+                                break 2;
+                            }
+                            break;
+
+                        case 'text':
+                        case 'email':
+                            if (empty($hasinput)) {
+                                $hasinput = $input->getAttribute('name');
+                            } else {
+                                // This isn't it. Skip to the next form.
+                                $hasinput = false;
+                                error_log('[client ' . getremoteaddr() . "]  $CFG->wwwroot  Form has multiple text fields");
+                                break 2;
+                            }
+                            break;
+
+                        case 'reset':
+                            continue;
+                            break;
+
+                        default:
+                            // Anything else, we just pass through its value.
+                            if ($input->getAttribute('name')) {
+                                $formvalues[$input->getAttribute('name')] = $input->getAttribute('value');
+                            }
+                            break;
+                    }
+                }
+
+                // Did this form make sense?
+                if (!empty($formurl) && !empty($hasinput) && !empty($haspassword)) {
+                    $validform = true;
+                    break;
+                }
+            }
+
+            if (!$validform) {
+                error_log('[client ' . getremoteaddr() . "]  $CFG->wwwroot  Could not find login form from Shibboleth");
+                @unlink($cookiefile);
+                return false;
+            }
+
+            // 3. Submit the login form and see where we get redirected back to.
+            // 3a. We probably only got a partial URL from the form. Let's fix that.
+            if (strpos($formurl, '//') === 0) {
+                $formurl = 'https' . $formurl;
+            } else if ($formurl[0] == '/') {
+                $urlparts = parse_url($loginpageinfo['url']);
+                $domain = !empty($urlparts['scheme']) ? $urlparts['scheme'] : 'https';
+                $domain .= '://' . $urlparts['host'] . (!empty($urlparts['port']) ? ':' . $urlparts['port'] : '');
+                $formurl = $domain . $formurl;
+            }
+
+            $formvalues[$hasinput] = $username;
+            $formvalues[$haspassword] = $password;
+
+            $mappedformvalues = array();
+            foreach ($formvalues as $k => $v) {
+                $mappedformvalues[] = rawurlencode($k) . '=' . rawurlencode($v);
+            }
+
+            $response = $curl->post($formurl, implode('&', $mappedformvalues), $options);
+
+            // Did we come back to ourselves?
+            $result = false;
+            $responseinfo = $curl->get_info();
+            if (strpos($responseinfo['url'], $CFG->wwwroot) === 0) {
+                // We were outright redirected back to somewhere in Moodle. This has to be successful.
+                $result = true;
+            } else if (!empty($responseinfo['redirect_url']) && strpos($responseinfo['redirect_url'], $CFG->wwwroot) === 0) {
+                // We were meant to be redirected back to somewhere in our Moodle - also successful.
+                $result = true;
+            } else {
+                // Some IdPs don't do anything so fancy. They leave us a form to click on.
+                $dom = new DOMDocument();
+                if ($dom->loadHTML($response)) {
+                    foreach ($dom->getElementsByTagName('form') as $form) {
+                        $action = $form->getAttribute('action');
+                        $action = html_entity_decode($action, ENT_QUOTES | ENT_HTML5);
+                        if (strpos($action, $CFG->wwwroot) === 0) {
+                            $result = true;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            @unlink($cookiefile);
+
+            // We also need to flag to the rest of the Shib auth library that we've taken this route.
+            // And do so in a way that cannot be influenced any other way.
+            if ($result) {
+                define('SHIB_AUTH_BACKGROUND', 1);
+            }
+            return $result;
         } else {
             // If we are not, the user has used the manual login and the login name is
             // unknown, so we return false.
@@ -91,7 +247,19 @@ class auth_plugin_shibboleth extends auth_plugin_base {
         }
     }
 
+    /**
+     * Indicates if Moodle should update internal user records from external sources.
+     * Usually we should in the case of Shibboleth but not if we've merely authenticated for a token.
+     *
+     * @return bool true means automatically copy data from ext to user table.
+     */
+    function is_synchronised_with_external() {
+        if (defined('SHIB_AUTH_BACKGROUND') && SHIB_AUTH_BACKGROUND) {
+            return false;
+        }
 
+        return parent::is_synchronised_with_external();
+    }
 
     /**
      * Returns the user information for 'external' users. In this case the
diff --git a/auth/shibboleth/lang/en/auth_shibboleth.php b/auth/shibboleth/lang/en/auth_shibboleth.php
index 8f57bc29..544a7ce1 100644
--- a/auth/shibboleth/lang/en/auth_shibboleth.php
+++ b/auth/shibboleth/lang/en/auth_shibboleth.php
@@ -27,6 +27,8 @@ $string['auth_shib_auth_method'] = 'Authentication method name';
 $string['auth_shib_auth_method_description'] = 'Provide a name for the Shibboleth authentication method that is familiar to your users. This could be the name of your Shibboleth federation, e.g. <tt>SWITCHaai Login</tt> or <tt>InCommon Login</tt> or similar.';
 $string['auth_shib_contact_administrator'] = 'In case you are not associated with the given organizations and you need access to a course on this server, please contact the <a href="mailto:{$a}">Moodle Administrator</a>.';
 $string['auth_shibbolethdescription'] = 'Using this method users are created and authenticated using <a href="http://shibboleth.internet2.edu/">Shibboleth</a>.<br />Be sure to read the <a href="../auth/shibboleth/README.txt">README</a> for Shibboleth on how to set up your Moodle with Shibboleth';
+$string['auth_shibboleth_background'] = 'Attempt to log into Shibboleth in the background.';
+$string['auth_shibboleth_background_description'] = 'When using Shibboleth with Web Services, might need to have Moodle authenticate for you in the background. As this is not 100% reliable with all Shibboleth services, it needs to be specifically enabled.';
 $string['auth_shibboleth_errormsg'] = 'Please select the organization you are member of!';
 $string['auth_shibboleth_login'] = 'Shibboleth login';
 $string['auth_shibboleth_login_long'] = 'Login to Moodle via Shibboleth';
diff --git a/auth/shibboleth/settings.php b/auth/shibboleth/settings.php
index c9013783..e71f57c9 100644
--- a/auth/shibboleth/settings.php
+++ b/auth/shibboleth/settings.php
@@ -37,6 +37,11 @@ if ($ADMIN->fulltree) {
     $settings->add(new admin_setting_configtext('auth_shibboleth/user_attribute', get_string('username'),
             get_string('auth_shib_username_description', 'auth_shibboleth'), '', PARAM_RAW));
 
+    // Authenticate in background.
+    $settings->add(new admin_setting_configcheckbox('auth_shibboleth/auth_background',
+            get_string('auth_shibboleth_background', 'auth_shibboleth'),
+            get_string('auth_shibboleth_background_description', 'auth_shibboleth'), ''));
+
     // COnvert Data configuration file.
     $settings->add(new admin_setting_configfile('auth_shibboleth/convert_data',
             get_string('auth_shib_convert_data', 'auth_shibboleth'),
