docs: function login(credentials, loginOptions) {
      if (loginOptions === void 0) { loginOptions = {}; }
      var wasOfflineLogin = false;
      var serverUrl = urls.resolveServer('/', loginOptions);
      var serverObj = server.Server.getInstance(serverUrl);
      if (serverObj.sessionUserUuid) {
          // logged in already
          return logout({
              serverUrl: serverUrl
          }).then(function () {
              diag.debug.assert(function () { return !serverObj.sessionUserUuid; });
              return login(credentials, loginOptions); // repeat after logout
          });
      }
      else if (serverObj.credentials) {
          // had credentials but no session, so we were logged in offline
          wasOfflineLogin = true;
      }
      // process options
      var currentOptions = serverObj.applyOptions({
          serverUrl: serverUrl,
          agentOptions: loginOptions.agentOptions || init.initOptions.agentOptions,
          agentClass: loginOptions.agentClass || init.initOptions.agentClass,
          // options taking effect at login time
          clientApp: loginOptions.clientApp || init.initOptions.clientApp,
          logonCallback: loginOptions.logonCallback || init.initOptions.logonCallback,
          clientCertificate: loginOptions.clientCertificate || init.initOptions.clientCertificate
      });
      var logonCallback = currentOptions.logonCallback || _.identity;
      return ajax(_.defaults({
          serverUrl: serverUrl,
          method: 'POST',
          url: '/gofer/security/rest/auth/login',
          body: credentials
      }, currentOptions)).then(function (response) {
          // real physical logon, ajax call sets sessionUserUuid
          if (!serverObj.sessionUserUuid) {
              diag.debug.warn('BUG: Relution did not set X-Gofer-User response header');
              serverObj.sessionUserUuid = response.user.uuid;
          }
          diag.debug.assert(function () { return serverObj.sessionUserUuid === response.user.uuid; });
          return response;
      }, function (error) {
          // offline login response
          if (!error.statusCode && loginOptions.offlineCapable) {
              // ajax timeout -> offline login attempt
              diag.debug.assert(function () { return !serverObj.sessionUserUuid; }, 'no physical login, as otherwise logonCallback would be executed');
              return offline.fetchOfflineLogin(credentials, currentOptions).then(function (loginResponse) {
                  if (!loginResponse) {
                      // when there is no persistent data available, aka. this is the initial login attempt,
                      // keep saying the server is offline...
                      return Q.reject(error);
                  }
                  return loginResponse;
              }, function (offlineError) {
                  // most likely the password entered was incorrect,
                  // make sure the offlineError indicates the server is unavailable as well
                  diag.debug.assert(function () { return !offlineError.statusCode; });
                  diag.debug.assert(function () { return !offlineError.requestUrl; });
                  offlineError.requestUrl = error.requestUrl;
                  diag.debug.assert(function () { return !offlineError.cause; });
                  offlineError.cause = error;
                  // we rethrow the annotated error of decoding the stored response,
                  // because the network error just indicates we are offline and does
                  // not mention the credentials being incorrect as this one does...
                  return Q.reject(offlineError);
              });
          }
          else if (error.statusCode && wasOfflineLogin) {
              // server side rejection, clear login data so that subsequent offline logins fail as well
              return offline.clearOfflineLogin(credentials, currentOptions).catch(function (offlineError) {
                  // this is bad but we can not do much about it
                  diag.debug.warn('failed erasing offline login data', offlineError);
                  return Q.reject(error);
              });
          }
          return Q.reject(error);
      }).then(function (response) {
          // switch current server
          if ('roles' in response.roles) {
              // fixes a defect of Java implementation
              response.roles = response.roles['roles'];
          }
          serverObj.authorization = {
              name: response.user.uuid,
              roles: _.map(response.roles, function (role) { return role.uuid; })
          };
          serverObj.organization = response.organization;
          serverObj.user = response.user;
          serverObj.credentials = credentials;
          server.setCurrentServer(serverObj);
          return response;
      }).then(function (response) {
          // this is the earliest point at which library state reflects correct authorization, etc.
          // Thus, the logonCallback may execute here now, but only if we are online actually...
          if (!serverObj.sessionUserUuid) {
              return response; // offline
          }
          // we have a session logged into this user
          diag.debug.assert(function () { return serverObj.sessionUserUuid === server.getCurrentAuthorization().name; });
          // run logonCallback on response data and eventually store resultant object for offline login,
          // because this way the callback may add information to the response object that will also be
          // persisted and made available again when offline!
          return Q(logonCallback(response)).then(function (logonInfos) {
              if (logonInfos === void 0) { logonInfos = response; }
              if (logonInfos && logonInfos !== response) {
                  // any data returned by the logonCallback may be stored here
                  response.logonInfos = logonInfos;
              }
              // store offline login response
              if (loginOptions.offlineCapable || wasOfflineLogin) {
                  // initial store or update of login data
                  return offline.storeOfflineLogin(credentials, currentOptions, response).catch(function (offlineError) {
                      diag.debug.warn('offline login store failed', offlineError);
                      return response;
                  });
              }
              return response;
          }, function (error) {
              // logon callback failed, must logout to avoid making ajax calls in an unknown backend state
              return logout({
                  serverUrl: serverUrl
              }).catch(function (logoutError) {
                  diag.debug.error('failed to logout after login failure', logoutError);
                  return Q.reject(error);
              }).finally(function () {
                  // logout processing must leave us with no user session
                  diag.debug.assert(function () { return !serverObj.sessionUserUuid; });
              });
          });
      });
  }