Users, Registration, and Authentication [ 82 ] If they have submitted the form, we check the registration, to see if the values are valid: if( $this->checkRegistration() == true ) { If the form was completed properly, we can then process the registration and create the user account and the prole: $userId = $this->processRegistration(); if( $this->activeValue == 1 ) { If we have set all users to be active by default, we log the user in automatically: $this->registry->getObject('authenticate')- >forceLogin( $this->submittedValues['register_ user'], md5( $this->submittedValues['register_ password'] ) ); } $this->uiRegistrationProcessed(); } else { If the registration attempt wasn't successful, we display the user interface, passing a parameter to indicate that errors need to be displayed. $this->uiRegister( true ); } } Finally, if the user is just viewing the registration form, we simply show them that (courtesy of the uiRegister method). else { $this->uiRegister( false ); } CAPTCHA We don't want our social network to get clogged up with automated signups that aren't going to add anything to our site. We can use a CAPTCHA (Completely Automated Public Turing test to tell Computers and Human Apart) challenge to test that the sign up is a genuine person. A CAPTCHA challenge is often a series of words embedded in an image, many computer systems can't automatically pick up the text from this image, whereas a human can, helping to tell which signup is a human and which is an automated computer. Download from Wow! eBook <www.wowebook.com> Chapter 3 [ 83 ] General CAPTCHA Generally, CAPTCHA systems work by: • Generating a random phrase or string. • Storing this phrase in the user's session (so they can't see it, but we have a persistent copy as they move from the registration form, to process their registration. This is also useful as the image is generally generated by a separate HTTP request—so the session is needed to maintain the value). • Displaying a slightly distorted version of the phrase on the registration form within an image. • The user enters the text from the image into a text box. • When they submit the registration form, we compare this value to the value of the appropriate session eld—if they match, it passes. reCAPTCHA reCAPTCHA is a widely used CAPTCHA solution, we will look at implementing this in Chapter 12, Deployment, Security, and Maintenance. Where do I sign up? So we have all of this excellent sign up functionality, however, we need a template for our view! Below is code for our views/default/templates/authenticate/ register/main.tpl.php file . This code contains HTML elds for all of the elds we have set in the registration controller and its extension: <div id="main"> <div id="rightside"> </div> <div id="content"> <h1>Join DINO SPACE!</h1> If the user makes a mistake, we need to list any issues. To allow this, we have a template tag that is replaced with the errors list if there are errors. If there are no errors, the tag is removed: {error} <form action="authenticate/register" method="post"> Download from Wow! eBook <www.wowebook.com> Users, Registration, and Authentication [ 84 ] The values for these elds are then set to what the user had typed in when they submitted the form, saving them the need to re-enter all of the data that was actually correct. We can also add a tag to change the style of the label to indicate a problem if we wish: <label for="register_user">Username</label><br /> <input type="text" id="register_user" name="register_user" value="{register_user}" /><br /> <label for="register_password">Password</label><br /> <input type="password" id="register_password" name="register_password" value="" /><br /> <label for="register_password_confirm">Confirm password</label><br /> <input type="password" id="register_password_confirm" name="register_ password_confirm" value="" /><br /> <label for="register_email">Email</label><br /> <input type="text" id="register_email" name="register_email" value="{register_email}" /><br /> <label for="register_dino_name">Name of dinosaur</label><br /> <input type="text" id="register_dino_name" name="register_dino_name" value="{register_dino_name}" /><br /> <label for="register_dino_breed">Breed of dinosaur</label><br /> <input type="text" id="register_dino_breed" name="register_dino_breed" value="{register_dino_breed}" /><br /> <label for="register_dino_gender">Gender of dinosaur</label><br /> <select id="register_dino_gender" name="register_dino_gender"> <option value="male">male</option> <option value="female">female</option> </select><br /> <label for="register_dino_dob">Dinosaurs Date of Birth (dd/mm/yy)</ label><br /> <input type="text" id="register_dino_dob" name="register_dino_dob" value="{register_dino_dob}" /><br /> <label for="">Do you accept our terms and conditions?</label><br /> <input type="checkbox" id="register_terms" name="register_terms" value="1" /> <br /> Download from Wow! eBook <www.wowebook.com> Chapter 3 [ 85 ] <input type="submit" id="process_registration" name="process_ registration" value="Create an account" /> </form> </div> </div> Now, assuming we have added authenticate as a controller in our controllers table in the database (so the framework knows to pass control to it), we can go to http://ourwebsite/authenticate/register to create an account, and we are presented with the following registration screen: As well as this template, we need an error template, for any error messages to be inserted into, and a complete template, to thank the user for joining. These templates ( views/default/templates/authenticate/register/*.tpl.php), as well as the header and footer, are included in the code accompanying this chapter. Download from Wow! eBook <www.wowebook.com> Users, Registration, and Authentication [ 86 ] E-mail verication With CAPTCHA implemented, we know that our user is a human, however, we should still try and verify their e-mail address; there are a number of reasons for this, including: • Preventing a user from signing up multiple times. • Ensuring our records are up to date—particularly useful if a user forgets their password or e-mail address. • If the user is troublesome, we have more ability to prevent repeat-sign ups (unless they have multiple e-mail addresses), and most ISP's have an abuse e-mail account we can contact to report such users. • Adding value—when users build relationships through our site, or send messages to each other, they may want to receive e-mail notications. If we don't have their valid e-mail address, then they won't get these, and they may lose interest in the site, when their own network is expanding without their knowledge. Sending e-mails As we are developing a social network, we will need to frequently send e-mails, not just for e-mail verication, but also for reminding users of their details, informing them of users who are connecting with them, and sending news updates. To make this easier, we should create a simple class to manage e-mail sending. The code for this class is in the mailout.class.php le in the code that accompanies this chapter; however, let's have a look at some of the code. This class is based on the template manager class, in that it includes a template le, and replaces certain tags with the data that we supply. The main difference is we don't have a page object, and instead of being output to the browser, it is e-mailed to our user. Another difference with our template handler is that once we have sent a series of templates to the browser, the handler has completed its job. With the e-mail object, we may wish to send more than one e-mail during a single execution of the script. To accommodate this, we use the startFresh() method. This method contains code that would be more suited to the constructor, but is called before each new e-mail we send, wiping the e-mail contents. public function startFresh() { // not in constructor because object is reused, so this is done on each "new email" $this->lock = false; Download from Wow! eBook <www.wowebook.com> Chapter 3 [ 87 ] $this->error = 'Message not sent because: '; $this->message = ''; } When sending an e-mail, we are more often than not, going to be sending it to a user of the site, or to the contact of a user. One concern with an e-mailing code, is sending automated spam. We can detect for this, by searching for text designed to create new headers (that is, setting new recipients, or recipients to be carbon-copied in on the e-mail). /** * Sets the recipient * @param String the recipient * @return bool */ public function setTo( $to ) { If the e-mail address contains header characters, it is rejected: if(eregi("\r",(urldecode($to))) || eregi("\n",(urldecode($to)))) { // bad - header injections $this->lock(); $this->error .= ' Receipient Email header injection attempt, probably caused by spam attempts'; return false; } If the e-mail address does not meet the standard format of an e-mail address, it is also rejected: elseif( ! eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a- z0-9-]+)*(\.[a-z]{2,3})$", $to) ) { // bad - invalid email $this->lock(); $this->error .= ' Recipient Email address no valid'; return false; } Download from Wow! eBook <www.wowebook.com> Users, Registration, and Authentication [ 88 ] Otherwise, we can send our e-mail: else { //good - let's do it! $this->to = $to; return true; } } The content of the e-mail is built from a number of e-mail templates: public function buildFromTemplates() { $bits = func_get_args(); $content = ""; foreach( $bits as $bit ) { if( strpos( $bit, 'emailtemplates/' ) === false ) { $bit = 'emailtemplates/' . $bit; } if( file_exists( $bit ) == true ) { $content .= file_get_contents( $bit ); } } $this->message = $content; } Template tags are replaced within the e-mail templates, in a similar way to the template manager: public function replaceTags( $tags ) { // go through them all if( sizeof($tags) > 0 ) { foreach( $tags as $tag => $data ) { // if the tag is an array, then we need to do more than a simple find and replace! Download from Wow! eBook <www.wowebook.com> Chapter 3 [ 89 ] if( ! is_array( $data ) ) { // replace the content $newContent = str_replace( '{' . $tag . '}', $data, $this->message ); // update the pages content $this->message = $newContent; } } } } When it comes to sending the e-mail, we simply check that there are no "locks" caused by errors we have encountered, and then perform a simple mail() call to send the e-mail: /** * Sends the email using Send Mail * @return void */ public function sendWithSendmail() { if($this->lock == true) { return false; } else { if( ! @mail($this->to, $this->subject, $this->message, $this->headers) ) { $this->error .= ' problems sending via PHP\'s mail function'; return false; } else { return true; } } } Download from Wow! eBook <www.wowebook.com> Users, Registration, and Authentication [ 90 ] True or false is returned, so we know if our mail object was successful in its e-mail attempt, allowing us to either inform the user, or store a log of the error somewhere for the administrator, if we wish. Room for improvement As with everything, there is room for improvement in this code, for instance: • The only mail delivery method it uses is PHP's mail() function • Mails are sent instantly—if our system is sending lots of e-mails frequently, we may wish to integrate this with a queuing system • Only plain text e-mails are sent (HTML e-mails can be sent using this, but this is a messy way to send HTML e-mails) Sending the e-mail verication e-mail With suitable functionality in our framework to send e-mails, how would we go about sending a verication e-mail to our new user? 1. Set the user to inactive. 2. Generate a random string, and assign it to the user. This is the verication key. 3. E-mail the user a link that includes their user ID and the verication key. 4. When they click on the link, we verify the verication key, and if appropriate, update their user account. Authentication with our authentication object With our user authentication object in place in our registry, we are now able to link into this to determine whether the current user is a logged in user, or not, and if they are we can also log them out. Logging in One of the rst things our framework should do, once it has connected to the database, is perform authentication checks. This should do one of two things; it should either check the current user's session data to see if we potentially have a user who is already logged in. If this is the case, it should perform checks to see if they are a valid user, and build up the user object as appropriate. If this is not the case, it should check to see if certain form elds have been submitted (such as username Download from Wow! eBook <www.wowebook.com> Chapter 3 [ 91 ] and password); if they have been, it should check to see if these are valid, and if appropriate, authenticate the user: $registry->getObject('authenticate')->checkForAuthentication(); This isn't part of the objects constructor, because we need to connect to the database (which is done after we instantiate the authentication object) rst. Are we logged in? After calling our main authentication method within the authentication object, we now probably want to know whether our user is logged in or not. If they are not, we will give them an overview page about Dino Space, and why they should join, and give them access to the login page, the signup page, and some other generic pages of content, such as terms and conditions, contact us, privacy policy, and so on. If they are logged in, we will probably want to take them to their prole, from which they can check for recent activity and communicate with their contacts. if( $registry->getObject('authenticate')->isLoggedIn() ) { // } else { // } Logging out When a user is done with the site for the time being, we want them to be able to log out to prevent anyone else who shares their computer from being able to log in as them. This problem is often illustrated by many student users of Facebook, who leave their account signed in and their computer switched on in shared accommodation, only to nd their proles have been vandalised. Checking for a logout request can be handled by our authentication controller. This can simply check the URL to see if it contains a logout request, and if it does, it can logout the user, and redirect them to the homepage: private function logout() { $this->registry->getObject('authenticate')->logout(); $this->registry->getObject('template')- >addTemplateBit('userbar', 'userbar-guest.tpl.php'); $this->registry->getObject('template')- >buildFromTemplates('header.tpl.php', 'login.tpl.php', 'footer.tpl.php'); } Download from Wow! eBook <www.wowebook.com> . an e-mail address, it is also rejected: elseif( ! eregi("^[_a-z 0-9 -] +(.[_a-z 0-9 -] +)*@[a-z 0-9 -] +(.[a- z 0-9 -] +)*(.[a-z]{2,3})$", $to) ) { // bad - invalid email $this->lock(); . $this->registry->getObject('authenticate' )-& gt;logout(); $this->registry->getObject('template' )- >addTemplateBit('userbar', 'userbar-guest.tpl .php& apos;); . 'userbar-guest.tpl .php& apos;); $this->registry->getObject('template' )- >buildFromTemplates('header.tpl .php& apos;, 'login.tpl .php& apos;, 'footer.tpl .php& apos;);