2

I was wondering, how would one go about using Codeigniter's Form Validation with a Web Service? I'm using REST and cURL to connect with a Web Service and was trying to use the Form Validation on a login page. I can get it to return an error when either the Username or Password fields are blank, but am not sure how to use it to check that a Password matches. My cUrl code is as follows:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("X-DocuSign-Authentication: $header"));
curl_setopt($ch, CURLOPT_UNRESTRICTED_AUTH, 1);
$output = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$this->form_validation->set_rules('username', 'USERNAME', 'required|is_unique[username]');
$this->form_validation->set_rules('password', 'PASSWORD', 'required|matches[password]');
if ($this->form_validation->run() == FALSE) {
    curl_close($ch);
    $this->load->view('error_login');
} else {        
    $response = json_decode($output, true);
    $accountId = $response["loginAccounts"][0]["accountId"];
    $baseUrl = $response["loginAccounts"][0]["baseUrl"];
    $userName = $response["loginAccounts"][0]["userName"];
    curl_close($ch);
    $username = $userName;
    $docusignURL = $baseUrl;
    $docHeader = $header;
    $loggedIn = array('is_logged_in' => true);
    $this->session->set_userdata('username', $username);
    $this->session->set_userdata('docusignURL', $docusignURL);
    $this->session->set_userdata('docHeader', $docHeader);
    $this->session->set_userdata($loggedIn);
    redirect('/create_envelope', 'refresh');
}

Right now it's returning two Error Notices, "Undefined offset: 1" and "Undefined property: Login::$db". The latter because, I'm assuming, I'm not using a model to connect to any database. Any helpful advice would be outstanding.

EDIT: So I tried the callback route, but I'm getting 500 errors. Here is my entire controller code so far:

<?php
    class Login extends CI_Controller {

        function index() {
            $this->load->view('al_login');
        }

        function authenticate(){

            $post = $this->input->post();
            extract($post);

            $integratorKey = '****-********-****-****-****-************';       
            $header = "<DocuSignCredentials><Username>" . $username . "</Username><Password>" . $password . "</Password><IntegratorKey>" . $integratorKey . "</IntegratorKey></DocuSignCredentials>";
            $url = "https://demo.docusign.net/restapi/v2/login_information";

            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_HEADER, false);
            curl_setopt($ch, CURLOPT_HTTPHEADER, array("X-DocuSign-Authentication: $header"));
            curl_setopt($ch, CURLOPT_UNRESTRICTED_AUTH, 1);
            $output = curl_exec($ch);
            $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $response = json_decode($output, true);
            $accountId = $response["loginAccounts"][0]["accountId"];
            $baseUrl = $response["loginAccounts"][0]["baseUrl"];
            $userName = $response["loginAccounts"][0]["userName"];
            curl_close($ch);
            $username = $userName;
            $docusignURL = $baseUrl;
            $docHeader = $header;
            $loggedIn = array('is_logged_in' => true);
            $this->session->set_userdata('username', $username);
            $this->session->set_userdata('docusignURL', $docusignURL);
            $this->session->set_userdata('docHeader', $docHeader);
            $this->session->set_userdata($loggedIn);
            $this->form_validation->set_rules('username', 'USERNAME', 'required');
            $this->form_validation->set_rules('password', 'PASSWORD', 'required|callback_password_check');

            if ($this->form_validation->run() == FALSE) {
                $this->load->view('al_login');
            } else {
                $this->load->view('/create_envelope');
            }
        }

        function password_check($str){

            // send the user name to the service 
            if ( $this->authenticate($str) == true ) {
                return true; 
            } else { 
                // customize the message with something 
                $this->form_validation->set_message('password_check', 'The %s is incorrect. Please try again');
                return false;
            } 
        }       
    }

I'm new to REST,cURL and Codeigniter, so my knowledge could use help. Thanks to both of you so far. Any ideas why I'm getting 500 errors?

NEW EDIT: @Petre So this is the response from our web service:

Array ( 
    [loginAccounts] => Array ( 
        [0] => Array ( 
            [name] => ******** 
            [accountId] => ******* 
            [baseUrl] => https://demo.docusign.net/restapi/v2/accounts/******* 
            [isDefault] => true 
            [userName] => **** ******** 
            [userId] => ********-****-****-****-************ 
            [email] => ****@******.com
            [siteDescription] => 
        ) 
    ) 
)

Doesn't seem to be anything of the sort.

5
  • Is the flow you're going for the following? - user fills in data in a form - server then validates sent data - the valid data is sent off to a webservice Commented Oct 11, 2013 at 20:53
  • User fills in data, submits to web service, web service checks login information, sends back response. Basically the user is logging into the web service via the login form. Commented Oct 11, 2013 at 21:04
  • (looks more at your code) uh yeah and reread the form validation page - you need to understand what you are putting in there first :-) Commented Oct 11, 2013 at 22:23
  • hi - break out sending to the info to docusign IN A SEPARATE METHOD. its all jumbled together. then pull out the value of $user like i showed in my reply. then send username and password to docusign. same deal with your session calls - thats only relevant if everything else has worked so separate them. Commented Oct 12, 2013 at 21:21
  • I put both the call to DocuSign and the session info in separate methods within a Model, but it still isn't working right. Commented Oct 12, 2013 at 21:36

2 Answers 2

1

The main issue I see with the code above is the spaghetti factor, as it's blocking view on what's important. What are you trying to do?

  1. Display a form with a submit button.
  2. Receive data from form at a certain URL/action.
  3. Validate form.
    1. If data is invalid, display form and inform user of mistakes.
    2. If data is valid, send data to webservice.
      1. If data is invalid, redirect user to form and inform of invalid user/pass combo.
      2. If data is valid, save some data in the session to store the authentication factor.

Let's not focus on steps 1 and 2 since they are most likely already working. So let's discuss form validation.

$this->form_validation->set_rules('username', 'USERNAME', 'required|is_unique[username]');
$this->form_validation->set_rules('password', 'PASSWORD', 'required|matches[password]');

Remember, at this stage you're validating the form, you do not have access to a relational database (since the data is retrieved via webservice and ideally on a different sever) and you just want to make sure that the username and password fields in the form have been filled in. You just want to make sure that they are provided and perhaps, that they are of a particular length. Let's revisit the code above.

$this->form_validation->set_rules('username', 'USERNAME', 'required|min_length[5]');
$this->form_validation->set_rules('password', 'PASSWORD', 'required|min_length[5]');

Remember, you're only validating the data that you've received through your form at this stage - you don't care about what happens on the webservice end, that end worries about providing that service.

Next we need to see what we're going to do in case the form validation failed or succeeded. The basic plan that we have at the moment is:

if ($this->form_validation->run() == FALSE) {
    //Username or password field were not provided or they were too short.
    //Render form and display errors.
} else {
    //Username and password field have been provided and they were within the length requirements.
    //Call cURL library and call webservice.
    //Retrieve webservice response.

    if ($response == 'valid') {
        //Webservice told us that username/password combo was right!
        //Store some data in the session to authenticate user.
    } else {
        //Webservice response told us that username/password combo is not right.
        //Render login form and inform user that user/pass combo were not correct.
    }
}

We just have comments for now - that's our plan, right? We've validated that we got some proper input from the user (input sanitizing) and now we want to see if those values were correct.

But now, instead of adding in all those blocks of code, let's do some good design and use models for it!

if ($this->form_validation->run() == FALSE) {
    //Username or password field were not provided or they were too short.

    $this->load->view('myview');
} else {
    //Username and password field have been provided and they were within the length requirements.

    $username = $this->input->post('username');
    $password = $this->input->post('password');

    // Make call to webservice with parameters to check if the credentials are correct or not.
    $response = $this->muser->checkLoginCredentials($username, $password)

    if ($response->valid == 'valid') {
        //Webservice told us that username/password combo was right!

        $this->muser->storeLoginCredentials($response->fullName, $response->email, $response->username);
    } else {
        //Webservice response told us that username/password combo is not right.

        $data['error'] = 'Invalid username/password combination';
        $this->load->view('myview', $data);
    }
}

Our code is super clear now! We only need to create the Muser model and make sure that it has two methods:

  • checkLoginCredentials: makes a call to the webservice with a username and password which we know are sanitized and retrieves the response.
  • storeLoginCredentials: given some parameters from the webservice response, store that data in a session.

Now, why did your code not work the first time?

  • You had a is_unique[username] rule on the username, but you're not interacting with a database, which that is used for. You're not checking if the username is unique, you just want a webservice to tell you if your login credentials are correct.
  • You also had a matches[password] rule on the password, which does nothing. You were perhaps thinking of having a _password_confirmation_ field, and yes, the rule would apply there.

Sorry for the long write-up, hope it helps and clears up more things along the way. If I can help with more info, comment and I'll update the answer!

Sign up to request clarification or add additional context in comments.

10 Comments

ok i'll bite :-) wouldn't it be better to use a callback? that way if they get the password wrong - you are still showing the username in the form. Ok lets say for whatever reason that does not work. Nested Ifs as clarity? how about the method does one thing - check the validity of the form values - and if true - go to next method of sending the values to service. much easier/cleaner to read.
if you are going to show $username = $this->input->post('username'); then go all the way and show $username = $this->input->post('username', TRUE); for xss clean.
and personal preference but i do think its better - if possible - always check for negative condition first. So like if response is NOT valid - then kick back the form. ELSE - go to a new method where you store the values - oh and show a view which you did not include :-)
all of this said with complete respect because you took the time to really dive into the question.
@cartalot Hey, responding to your comments, bottom-up. Good point about doing negative first, I agree with that. I didn't write up the view, nor did I write up the additional methods, since my answer is about design concerns, not technical implementation - I also don't look at solving problems and providing the full solution, merely explaining my view and how it would be done. I don't set XSS to TRUE for every parameter since you should always look at setting $config['global_xss_filtering'] to true.
|
0

i think what you want is named a "callback" see here http://ellislab.com/codeigniter/user-guide/libraries/form_validation.html#callbacks

First make sure your curl rest code is working correctly. Just have it echo out true or false or something simple so you verify its results.Then integrate it into your callback.

going from the example in the manual where they name it: callback_username_check

 function username_check($str){

 // send the user name to the service 
 if ( $this->_sendUserName($str) == true ) 
 { return true ; } 

 else{ 
  // customize the message with something 
  $this->form_validation->set_message('username_check', 'The %s is incorrect so have another cup of coffee and try again');
  return false ; } 

 }

Note that i'm calling a separate method that just has the rest/curl code. that way if your service changes, you don't have to change the callback.

edit - i just used the example of "username" to make it consistent with the example in the guide.

if you need to send both username and password to the service, my suggestion would be to do basic validation on the username, and then do the callback for the password. so the value for the password is coming from the callback and then get the value of username from

   $username = $this->input->post('username', TRUE);

you can use $this->input->post() anywhere before or after validation. the TRUE xss cleans it.

usually if someone messes up a user name & password, its the password that is incorrect. so by doing the validation with a callback, if it fails - when you show the form again - the user name will still be filled out. AND if you are getting information back from the service as to why it failed - you can customize the error message that is displayed with that information.

1 Comment

Could this also be done for the password? The login does work correctly. I was testing it prior to starting this whole validation issue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.