Effortless Flex 4 Development: Adding Authentication
Adding Authentication
I try to be as mindful as I can of security whenever I write books (while simultaneously not overwhelming the reader): For the most part, you cannot be too careful. In the services developed in this chapter, and in Chapter 8, there is a glaring security hole: As it stands, PHP scripts exist on the server that can manipulate a database without any user authentication whatsoever. This is not good. Real-world services not intended for public use should implement authentication and authorization. In the next couple of pages, I'll explain a few of the options for implementing authentication and authorization. As any one of these options would require pages and pages to fully develop, I'll focus on the fundamental theories and code required by each, without walking through all the gritty details.
Using .htaccess
The Apache Web server uses .htaccess files to configure how the server runs. One thing you can do with an .htaccess file is restrict access to an entire directory to an authenticated user. That code might look like this at its most basic:
AuthType Basic AuthName "Password Required" AuthUserFile /www/passwords/password.file
Normally the username-password combinations are stored in special text files on the server. When you attempt to access a protected directory, you'll be presented with a prompt for inputting your username and password (Figure 9.27). For the remainder of the browser session, you'll then be able to access the contents of that directory.
Of course, a Flash client can't "see" that prompt, let alone "type" in it, but there is a workaround: You can pass the username and password in the request by changing the server URL to http://username:password@services.example.com.
This will work, but you probably don't want to hard-code your credentials into the client application. First of all, if you change the access information, you'll need to recompile the application. Second, Flex applications can easily be decompiled, allowing someone to see the underlying code, thereby making the security measure meaningless.
An alternative, then, would be to use two TextInputs to take the username and password information from the user, then to programmatically add that to the service URL. This would require manipulating the service component in ActionScript:
serviceName.url = 'http://' + usernameTextInput.text serviceName.url += ':' + passwordTextInput; serviceName.url += '@services.example.com';
That code assumes that an MXML HTTPService component with an id of serviceName exists. If you wanted to create one in ActionScript, you'd just start with
var service:HttpService = new HttpService( );
Using PHP Sessions
The .htaccess approach works if you're using Apache, can create or edit .htaccess files, and want to restrict access to an entire directory. But if all of those don't apply to you, or if you want to restrict access based upon the specific user or user type, you'll need to implement the authorization within PHP. This isn't as hard as one might initially think.
To start, you'd just need to create a PHP login script that expects a username and password to be posted to it. This would be exactly like an HTML login page, without all of the HTML. The username and password would be validated, probably against a database, and, if correct, a new session would be started and whatever variables required would be stored in that session. The Flash client again would need to take the username and password from the client, then send those to the PHP login script using an HTTPService.
When the PHP script creates the session, by default the session ID will be sent to the client in a cookie. Every request from the client to the server will automatically include that session ID, because that's how cookies work in a browser-server dynamic. In fact, if you are using the Network Monitor in Flash Builder, you can see that the PHP session cookie is part of each request, regardless of the request type (Figure 9.28).
When using sessions, you could then write checks into your PHP scripts so that certain actions can only be taken by authorized users:
public function deleteProduct($id) { if (isset($_SESSION['usertype']) && ($_SESSION['usertype'] == 'admin')) { // Proceed } else { return null; } }
Generally speaking one shouldn't pollute a class file with secondary code like this, so you could make it cleaner by creating an authorized( ) method that performs the security check based upon the desired action:
public function deleteProduct($id) { if ($this->authorized('delete')) { // Proceed } else { return null; } }
That authorized( ) method might be defined like this:
public function authorized($action) { // Check for the user type: if (isset($_SESSION['usertype'])) { $usertype = $_SESSION['usertype']; } else { return false; // Invalid if no session variable! } // Create allowed actions by user type: $allowed['admin'] = array('create', 'retrieve', 'update', 'delete'); $allowed['yogi'] = array('create', 'retrieve', 'update'); $allowed['user'] = array('retrieve'); // Validate the action/user: if (in_array($action, $allowed[$usertype])) { return true; } else { return false; } } // End of method.
Using PHP Tokens
If you don't want to go the full session route, you can use tokens instead. This is essentially the same premise as a session without formally using sessions. The process goes like this:
- The Flash client sends the access credentials to a PHP script via a service.
The PHP script validates the credentials and creates a unique identifier.
You can create a unique identifier by applying PHP's md5( ) or sha1( ) function to any piece of data, like the current timestamp or a random number. The unique identifier will act as a representation of the authenticated user, so it would need to be stored on the server somehow, probably in a database.
- The PHP script returns the identifier (i.e., the token) to the Flash client.
- The Flash client stores the token in a local variable, then includes that token in every request of the server.
The PHP script would then validate the received token against the database prior to taking any actions.
As with my sessions example, you could create a special class method expressly for this purpose.