We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 37946
    • 70 Posts
    Hello!
    I am familiar with MODx development from a traditional web standpoint. However now I am attempting to use MODx as a user management tool, and using phonegap to build a mobile application.

    With phonegap, the entire "site" is sitting inside of an html file "index.html". All php requests need to be via ajax or some other javascript type action. No problem.

    I haven't worked exclusively with the MODx API in the past so I'm treading new ground both with MODx & phonegap.

    I am searching for a way to authenticate users via the MODx API. I am able to hook into the API and check if a user is authenticated, but just not sure where to start otherwise.

    I see that I am able to call a snippet via my external php file. Which is great! I thought about the calling the login snippet. However, I (think) it's not quite the solution. Since I can't really redirect or anything like that, I just need the ability to look up provided credentials and validate. If success - I'll handle the action.

    Any thoughts on how to authenticate users via MODx API?

    Thank you for your time!
      • 37946
      • 70 Posts
      I got this to work. I'm going to paste the code here in hopes that it will either help someone else, or others can chime in (please do) and show how I can improve the process.

      Everything has to live on index.html so...

      index.html
      <div data-role="page" id="login" data-url="login">  
                  <header data-role="header"> :</header>
                  
                  <div data-role="content">
                      <div class="form">
                          <form id="frm_login" action="" method="post">
                              <fieldset>
                              <ol>
                                  <li><label for="username" class="ui-hidden-accessible">Username*</label><input type="text" name="username" id="username" placeholder="Username" /></li>
                                  <li><label for="password" class="ui-hidden-accessible">Password</label><input type="password" name="password" id="password" placeholder="Password" /></li>
                                  <li><input type="hidden" name="returnUrl" value="" /></li>
                                  <li><input type="hidden" name="service" value="login" /></li>
                              </ol>
                              </fieldset>
      
                              <fieldset class="submit">
                                  <input type="submit" id="btn_submit" name="btn_submit" value="Login" /><span>or <a href="">Forgot Password?</a></span> 
                              </fieldset>
                          </form>    
                      </div>
                  </div>
                  
                  <footer data-position="fixed" data-role="footer"> </footer>
              </div>
      


      Here is the jquery that listens for the form submit (login form). This javascript lives in the head of index.html
      $('#frm_login').submit(function()
                          {  
                              var form_data = $("#frm_login").serialize();
                              
                              $.ajax({
                                  type    : 'post',
                                  url     : $env+'application/modx_login.php',
                                  datatype: 'json',
                                  data    : form_data,
                                  timeout : 20000,
      	
                                  success: function(return_data)
                                  {                                
                                      if ( return_data == 'success' )
                                      {
                                          navigator.notification.alert('login successful');
                                          location.hash = '#mainmenu';
                                      }
                                  },
                                  
                                  error: function(return_data)
                                  {
                                      
                                  }
                             });
      
                              return false;
                          });
      


      Here is my external php file that handels the form upon submission:
      modx_login.php
      <?php
          
          switch( getenv('HTTP_HOST') )
          {
              case 'local.mysite.com':
                  require_once '/path/to/my/config/config.core.php';
                  require_once MODX_CORE_PATH.'model/modx/modx.class.php';
              break;
          }
          
          
          $modx = new modX();
          $modx->initialize('web');
          $modx->getService('error','error.modError');
          
          
          $isAuthenticated = $modx->user->hasSessionContext('web');
          
          if ( !$isAuthenticated )
          {   
              error_log( 'user is not logged in' );
              
              $params['errTpl'] = 'lgnErrTpl';
              
              $snippet = $modx->runSnippet('Login', $params);        
              $errors  = $modx->getPlaceholder('errors');
              
              if ( $errors )
              {
                  echo $errors;
              }
          }
          else
          {
              error_log( 'user is already logged in' );
          } 
      


      Finally, here is my slightly modified login snippet. This file is located here:
      /core/components/login/controllers/web/Login.php
      <?php
      /**
       * Login
       *
       * Copyright 2010 by Shaun McCormick <[email protected]>
       *
       * Login is free software; you can redistribute it and/or modify it under the
       * terms of the GNU General Public License as published by the Free Software
       * Foundation; either version 2 of the License, or (at your option) any later
       * version.
       *
       * Login is distributed in the hope that it will be useful, but WITHOUT ANY
       * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
       * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
       *
       * You should have received a copy of the GNU General Public License along with
       * Login; if not, write to the Free Software Foundation, Inc., 59 Temple
       * Place, Suite 330, Boston, MA 02111-1307 USA
       *
       * @package login
       */
      /**
       * Handles logging in and out of users
       *
       * @package login
       * @subpackage controllers
       */
      class LoginLoginController extends LoginController {
          public $isAuthenticated = false;
          /** @var LoginDictionary $dictionary */
          public $dictionary;
      
          public function initialize() {
              $this->setDefaultProperties(array(
                  'loginTpl'    => 'lgnLoginTpl',
                  'logoutTpl'   => 'lgnLogoutTpl',
                  'loginMsg'    => '',
                  'logoutMsg'   => '',
                  'preHooks'    => '',
                  'tplType'     => 'modChunk',
                  'actionKey'   => 'service',
                  'loginKey'    => 'login',
                  'logoutKey'   => 'logout',
                  'errorPrefix' => 'error',
                  'errTpl'      => 'lgnErrTpl',
                  'errTplType'  => 'modChunk',
                  'rememberMeKey' => 'rememberme',
                  'loginContext'  => $this->modx->context->get('key'),
                  'contexts'    => '',
              ));
      
              if (!empty($_REQUEST['login_context'])) {
                  $this->setProperty('loginContext',$_REQUEST['login_context']);
              }
              if (!empty($_REQUEST['add_contexts'])) {
                  $this->setProperty('contexts',$_REQUEST['add_contexts']);
              }
              $this->isAuthenticated = $this->modx->user->isAuthenticated($this->getProperty('loginContext'));
          }
      
          /**
           * Process the controller
           * @return string
           */
          public function process()
          {
      //        error_log( 'proccess() called');
      //        error_log( print_r($_REQUEST, true) );
      
              if ( !empty($_REQUEST[$this->getProperty('actionKey','service')]) )
              {
      //            error_log( 'action key: '.$_REQUEST[$this->getProperty('actionKey','service')] );
                  
                  $this->handleRequest();
              }
          }
      
          /**
           * Render the logout or login form
           * @return string
           */
          public function renderForm() {
              $redirectToPrior = $this->getProperty('redirectToPrior',false,'isset');
              $tpl = $this->isAuthenticated ? $this->getProperty('logoutTpl') : $this->getProperty('loginTpl');
              $actionMsg = $this->isAuthenticated
                  ? $this->getProperty('logoutMsg',$this->modx->lexicon('login.logout'))
                  : $this->getProperty('loginMsg',$this->modx->lexicon('login'));
      
              $this->modx->setPlaceholder('actionMsg', $actionMsg);
              $phs = $this->isAuthenticated ? $this->getProperties() : array_merge($this->getProperties(), $_POST);
              foreach ($phs as $k => $v) {
                  $phs[$k] = str_replace(array('[',']'),array('[',']'),$v);
              }
              /* make sure to strip out logout GET parameter to prevent ghost logout */
              $logoutKey = $this->getProperty('logoutKey','logout');
              if (!$redirectToPrior) {
                  $phs['request_uri'] = str_replace(array('?service='.$logoutKey,'&service='.$logoutKey,'&service='.$logoutKey),'',$_SERVER['REQUEST_URI']);
              } else {
                  $phs['request_uri'] = str_replace(array('?service='.$logoutKey,'&service='.$logoutKey,'&service='.$logoutKey),'',$_SERVER['HTTP_REFERER']);
              }
      
              /* properly build logout url */
              if ($this->isAuthenticated) {
                  $phs['logoutUrl'] = $phs['request_uri'];
                  $phs['logoutUrl'] .= strpos($phs['logoutUrl'],'?') ? ($this->modx->getOption('xhtml_urls',null,false) ? '&' : '&') : '?';
                  $phs['logoutUrl'] .= $phs['actionKey'].'='.$phs['logoutKey'];
                  $phs['logoutUrl'] = str_replace(array('?=','&='),'',$phs['logoutUrl']);
              }
      
              $this->loadReCaptcha();
      
              return $this->login->getChunk($tpl,$phs,$this->getProperty('tplType','modChunk'));
          }
      
          /**
           * Either output the content or set it as a placeholder
           * @param string $content
           * @return string
           */
          public function output($content = '') {
              $toPlaceholder = $this->getProperty('toPlaceholder','');
              if (!empty($toPlaceholder)) {
                  $this->modx->setPlaceholder($toPlaceholder,$content);
                  return '';
              }
              return $content;
          }
      
          /**
           * Check for and load reCaptcha
           * @return boolean
           */
          public function loadReCaptcha() {
              $loaded = false;
              $preHooks = $this->getProperty('preHooks','');
              /* if using recaptcha, load recaptcha html */
              if (strpos($preHooks,'recaptcha') !== false && !$this->isAuthenticated) {
                  /** @var reCaptcha $reCaptcha */
                  $reCaptcha = $this->modx->getService('recaptcha','reCaptcha',$this->login->config['modelPath'].'recaptcha/');
                  if ($reCaptcha instanceof reCaptcha) {
                      $this->modx->lexicon->load('login:recaptcha');
                      $recaptchaTheme = $this->getProperty('recaptchaTheme','clean');
                      $recaptchaWidth = $this->getProperty('recaptchaWidth',500);
                      $recaptchaHeight = $this->getProperty('recaptchaHeight',300);
                      $html = $reCaptcha->getHtml($recaptchaTheme,$recaptchaWidth,$recaptchaHeight);
                      $this->modx->setPlaceholder('login.recaptcha_html',$html);
                      $loaded = true;
                  } else {
                      $this->modx->log(modX::LOG_LEVEL_ERROR,'[Login] '.$this->modx->lexicon('login.recaptcha_err_load'));
                  }
              }
              return $loaded;
          }
      
          /**
           * Handle any POST request
           *
           * @return void
           */
          public function handleRequest()
          {
      //        error_log('handleRequest() called');
              
              $this->loadDictionary();
              
              $actionKey = $this->getProperty('actionKey','service');
              
              if ( !empty($_POST) && isset($_POST[$actionKey]) && !$this->isAuthenticated )
              {
                  error_log( 'validating user...' );
                  
                  if ( $_POST[$actionKey] == $this->getProperty('loginKey','login') )
                  {
                      $this->login();
                  }
                  else
                  {
                      $this->modx->log(modX::LOG_LEVEL_ERROR,$this->modx->lexicon('login.invalid_post',array(
                          'action' => $_POST[$actionKey],
                      )));
                  }
              }
              else
              {
                  error_log( 'logging user out...' );
                  
                  $this->logout();
              }
          }
      
          /**
           * Handle a Login submission
           * 
           * @return void
           */
          public function login()
          {
              /* set default POST vars if not in form */
              if ( empty($_POST['login_context']) )
              {
      //            error_log( ' login_context is empty so setting it to: '.$this->getProperty('loginContext') );
                  
                  $_POST['login_context'] = $this->getProperty('loginContext');
              }
      
              if ( $this->runPreLoginHooks() )
              {
                  $response = $this->runLoginProcessor();
                  
                  /* if we've got a good response, proceed */
                  if ( !empty($response) && !$response->isError() )
                  {
                      error_log( 'successfully logged in' );
                      
                      $this->runPostLoginHooks($response);
                      
                      /* process posthooks for login */
                      if ( $this->postHooks->hasErrors() )
                      {
                          error_log( 'postHooks has errors' );
                          
                          $errorPrefix = $this->getProperty('errorPrefix','error');
                          $this->modx->toPlaceholders($this->postHooks->getErrors(),$errorPrefix);
      
                          $errorMsg = $this->postHooks->getErrorMessage();
                          $this->modx->toPlaceholder('message',$errorMsg,$errorPrefix);
                      }
                      else
                      {                    
                          $this->redirectAfterLogin($response);
                      }
                  /* logout failed, output error */
                  }
                  else
                  {
                      $this->checkForRedirectOnFailedAuth($response);
                      
                      $errorOutput = $this->prepareFailureMessage($response, $this->modx->lexicon('login.login_err'));
                      
                      error_log( 'login failed' );
                      error_log( 'reason: '.$errorOutput );
                      
                      $this->modx->setPlaceholder('errors', $errorOutput);
                  }
              }
          }
      
          /**
           * Run any preHooks before logging in
           * 
           * @return boolean
           */
          public function runPreLoginHooks() {
              $success = true;
              
              /* do pre-login hooks */
              $this->loadHooks('preHooks');
              $this->preHooks->loadMultiple($this->getProperty('preHooks',''),$this->dictionary->toArray(),array(
                  'mode' => Login::MODE_LOGIN,
              ));
      
              /* process prehooks */
              if ($this->preHooks->hasErrors()) {
                  $this->modx->toPlaceholders($this->preHooks->getErrors(),$this->getProperty('errorPrefix','error'));
      
                  $errorMsg = $this->preHooks->getErrorMessage();
                  $errorOutput = $this->modx->parseChunk($this->getProperty('errTpl'), array('msg' => $errorMsg));
                  $this->modx->setPlaceholder('errors',$errorOutput);
                  $success = false;
              }
              return $success;
          }
      
          /**
           * @return modProcessorResponse
           */
          public function runLoginProcessor()    
          {
      //        error_log( 'runLoginProcessor() called' );
              
              $fields = $this->dictionary->toArray();
              
              /* send to login processor and handle response */
              $c = array(
                  'login_context' => $this->getProperty('loginContext'),
                  'add_contexts'  => $this->getProperty('contexts',''),
                  'username'      => $fields['username'],
                  'password'      => $fields['password'],
                  'returnUrl'     => $fields['returnUrl'],
                  'rememberme'    => !empty($fields[$this->getProperty('rememberMeKey','rememberme')]) ? true : false,
              );
              
              return $this->modx->runProcessor('security/login',$c);
          }
      
          /**
           * @param modProcessorResponse $response
           * @param string $defaultErrorMessage
           * @return string
           */
          public function prepareFailureMessage(modProcessorResponse $response,$defaultErrorMessage = '') {
              $errorOutput = '';
              $errTpl = $this->getProperty('errTpl');
              $errors = $response->getFieldErrors();
              $message = $response->getMessage();
              if (!empty($errors)) {
                  foreach ($errors as $error) {
                      $errorOutput .= $this->modx->parseChunk($errTpl, $error);
                  }
              } elseif (!empty($message)) {
                  $errorOutput = $this->modx->parseChunk($errTpl, array('msg' => $message));
              } else {
                  $errorOutput = $this->modx->parseChunk($errTpl, array('msg' => $defaultErrorMessage));
              }
              return $errorOutput;
          }
      
          /**
           * Check to see if the user wants to redirect to a separ
           * @param modProcessorResponse $response
           * @return void
           */
          public function checkForRedirectOnFailedAuth(modProcessorResponse $response) {
              $redirectToOnFailedAuth = $this->getProperty('redirectToOnFailedAuth',false,'isset');
              if ($redirectToOnFailedAuth && $redirectToOnFailedAuth != $this->modx->resource->get('id')) {
                  $p = array(
                      'u' => $this->dictionary->get('username'),
                  );
                  $message = $response->getMessage();
                  if (!empty($message)) $params['m'] = $message;
                  $url = $this->modx->makeUrl($redirectToOnFailedAuth,'',$p,'full');
                  $this->modx->sendRedirect($url);
              }
          }
      
          /**
           * Run any postHooks specified after the user has logged in
           * 
           * @param modProcessorResponse $response
           * @return void
           */
          public function runPostLoginHooks(modProcessorResponse $response) {
              $responseArray = $response->getObject();
      
              /* do post hooks */
              $postHooks = $this->getProperty('postHooks','');
              $this->loadHooks('postHooks');
              $fields = $_POST;
              $fields['response'] =& $responseArray;
              $fields['contexts'] =& $contexts;
              $fields['loginContext'] =& $loginContext;
              $fields['loginResourceId'] =& $loginResourceId;
              $this->postHooks->loadMultiple($postHooks,$fields,array(
                  'mode' => Login::MODE_LOGIN,
              ));
      
          }
      
          /**
           * Redirect the user after logging them in
           * 
           * @param modProcessorResponse $response
           * @return void
           */
          public function redirectAfterLogin(modProcessorResponse $response)
          {
              $responseArray = $response->getObject();
              
              /* allow dynamic redirection handling */
              $redirectBack = $this->modx->getOption('redirectBack',$_REQUEST,$this->getProperty('redirectBack',''));
              $redirectBackParams = $this->modx->getOption('redirectBackParams',$_REQUEST,$this->getProperty('redirectBackParams',''));
              
              if (!empty($redirectBackParams))
              {
                  $redirectBackParams = $this->login->decodeParams($redirectBackParams);
              }
              /* otherwise specify a specific resource to redirect to */
              $loginResourceId = $this->getProperty('loginResourceId',$redirectBack);
                      
              /* login posthooks succeeded, now redirect */
      
              if ( !empty($loginResourceId) )
              {
                  error_log('loginResourceID not empty' );
                 
                  $loginResourceParams = $this->getProperty('loginResourceParams',$redirectBackParams);
                  
                  if (!empty($loginResourceParams) && !is_array($loginResourceParams))
                  {
                      $loginResourceParams = $this->modx->fromJSON($loginResourceParams);
                  }
                  
                  $url = $this->modx->makeUrl($loginResourceId,'',$loginResourceParams,'full');
                  $this->modx->sendRedirect($url);
              }
              elseif ( !empty($responseArray) && !empty($responseArray['url']) ) 
              {
                  $this->modx->sendRedirect($responseArray['url']);
              }
              else
              {
                 // $this->modx->sendRedirect($this->modx->getOption('site_url'));
                 echo 'success';
                 exit;
              }
          }
      
          public function logout()
          {
              /* set default REQUEST vars if not provided */
              if ( empty($_REQUEST['login_context']) )
              {
                  $_REQUEST['login_context'] = $this->getProperty('loginContext');
              }
      
              if ( $this->runPreLogoutHooks() )
              {
                  /* send to logout processor and handle response for each context */
                  /** @var modProcessorResponse $response */
                  $response = $this->modx->runProcessor('security/logout',array(
                      'login_context' => $this->getProperty('loginContext',$this->modx->context->get('key')),
                      'add_contexts'  => $this->getProperty('contexts',''),
                  ));
      
                  /* if successful logout */
                  if ( !empty($response) && !$response->isError() )
                  {
                      error_log( 'successfully logged out' );
      
                      $this->runPostLogoutHooks($response);
                      $this->redirectAfterLogout($response);
                  /* logout failed, output error */
                  }
                  else
                  {
                      $errorOutput = $this->prepareFailureMessage($response,$this->modx->lexicon('login.logout_err'));
                      $this->modx->setPlaceholder('errors', $errorOutput);
                      
                      error_log( 'logout failed' );
                      error_log( 'reason: '.$errorOutput );
                  }
              }
          }
      
          /**
           * @return boolean
           */
          public function runPreLogoutHooks() {
              $success = true;
              $this->loadHooks('preHooks');
              $this->preHooks->loadMultiple($this->getProperty('preHooks',''),$this->dictionary->toArray(),array(
                  'mode' => Login::MODE_LOGOUT,
              ));
      
              if ($this->preHooks->hasErrors()) {
                  $this->modx->toPlaceholders($this->preHooks->getErrors(),$this->getProperty('errorPrefix','error'));
      
                  $errorMsg = $this->preHooks->getErrorMessage();
                  $errorOutput = $this->modx->parseChunk($this->getProperty('errTpl'), array('msg' => $errorMsg));
                  $this->modx->setPlaceholder('errors',$errorOutput);
                  $success = false;
              }
              return $success;
          }
      
          /**
           * Run any post-logout hooks
           *
           * @param modProcessorResponse $response
           * @return boolean
           */
          public function runPostLogoutHooks(modProcessorResponse $response) {
              $success = true;
              
              /* do post hooks for logout */
              $postHooks = $this->getProperty('postHooks','');
              $this->loadHooks('postHooks');
              $fields = $this->dictionary->toArray();
              $fields['response'] =& $response->getObject();
              $fields['contexts'] =& $contexts;
              $fields['loginContext'] =& $loginContext;
              $fields['logoutResourceId'] =& $logoutResourceId;
              $this->postHooks->loadMultiple($postHooks,$fields,array(
                  'mode' => Login::MODE_LOGOUT,
              ));
      
              /* log posthooks errors */
              if ($this->postHooks->hasErrors()) {
                  $this->modx->log(modX::LOG_LEVEL_ERROR,'[Login] Post-Hook errors: '.print_r($this->postHooks->getErrors(),true));
      
                  $errorMsg = $this->postHooks->getErrorMessage();
                  if (!empty($errorMsg)) {
                      $this->modx->log(modX::LOG_LEVEL_ERROR,'[Login] Post-Hook error: '.$errorMsg);
                  }
                  $success = false;
              }
              return $success;
          }
      
          /**
           * Redirect the user after they logout
           * 
           * @param modProcessorResponse $response
           * @return void
           */
          public function redirectAfterLogout(modProcessorResponse $response)
          {
              $responseArray = $response->getObject();
      
              /* redirect */
              $logoutResourceId = $this->getProperty('logoutResourceId',0);
          
              if ( !empty($responseArray) && !empty($responseArray['url']) )
              {
                  $this->modx->sendRedirect($responseArray['url']);
              }
              elseif ( !empty($logoutResourceId) )
              {
                  $logoutResourceParams = $this->getProperty('logoutResourceParams','');
                  
                  if (!empty($logoutResourceParams))
                  {
                      $logoutResourceParams = $this->modx->fromJSON($logoutResourceParams);
                  }
                  
                  $url = $this->modx->makeUrl($logoutResourceId,'',$logoutResourceParams,'full');
                  $this->modx->sendRedirect($url);
              }
              else
              {
      //            $this->modx->sendRedirect($_SERVER['REQUEST_URI']);
                  
                  echo 'success';
                  exit;
              }
          }
      }
      return 'LoginLoginController';
      


      Once I get the logout to work, I'll update the thread again. [ed. note: jentree last edited this post 11 years, 11 months ago.]
        • 5340
        • 1,624 Posts
        Maybe I'm missing something but I advise against changing the Login snippet. Preferably you should extend that class and modify it to your needs. This way an upgrade will not break your implementation.

          • 37946
          • 70 Posts
          Here is the logout piece...

          Again, everything lives on "index.html" so I just set up a javascript listener for my logout button:
          $('.logout').click(function()
                              {
          //                        navigator.notification.alert('logout clicked');
                                  
                                  $.post($env+'application/modx_logout.php?service="logout"', function(return_data)
                                  {
          //                            navigator.notification.alert(return_data);
                                      if ( return_data == 'success' )
                                      {
          //                                  navigator.notification.alert('login successful');
                                          location.hash = '#login';
                                      }
                                      
                                  });
                                  
                                  return false;
                              });
          


          modx_logout.php
          <?php
              
              switch( getenv('HTTP_HOST') )
              {
                  case 'local.elancobbuapp.com':
                      require_once '/path/to/my/config/config.core.php';
                      require_once MODX_CORE_PATH.'model/modx/modx.class.php';
                  break;
              }
              
              
              $modx = new modX();
              $modx->initialize('web');
              $modx->getService('error','error.modError');
              
              
              $isAuthenticated = $modx->user->hasSessionContext('web');
              
              if ( $isAuthenticated )
              {   
                  error_log( 'logging user out' );
                      
                  $snippet = $modx->runSnippet('Login', $params);        
                  $errors  = $modx->getPlaceholder('errors');
                  
                  if ( $errors )
                  {
                      echo $errors;
                  }
              }
              else
              {
                  error_log( 'user is not logged in' );
              } 
          


          I updated the "Login.php" file to include the updates I made for the logout function.
            • 37946
            • 70 Posts
            Sorry for all of the error log messages in there...I needed a lot of hand-holding.