I forget why, but a few days ago I started doing some digging around with authentication in Zend_Amf_Server. I had figured that I would add an adapter to the Zend_Amf_Server::setAuth() method and that would be it.
But I was wrong.
AMF allows for multiple request bodies to be sent at the same time. Of those there are several “special” types of commands. One of those commands is logging in. What this means is that you don’t need a method that logs someone in for you. Zend_Amf_Server handles authentication separately from your service classes.
Authentication for Zend_Amf_Server will generally use a combination of Zend_Auth and Zend_Acl components. Zend_Auth is used to provide the credential verification while Zend_Acl is used to validate that the current user user can access the requested service method. It is actually a relatively trivial task to restrict access to non-logged in users using the method that I will describe here.
The first step in the process is to create an authentication adapter. It really doesn’t matter what you’re using. What matters is that the adapter returns an identity object with a property called “role”. The built in ACL handle expects this to be part of the identity object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class Auth extends Zend_Amf_Auth_Abstract { const LOGGEDIN_ROLE = 'loggedin'; public function authenticate() { $identity = new stdClass(); $result = Zend_Auth_Result::FAILURE; // Do a proper login, y'all if ($this->_username == 'test' && $this->_password == 'test') { $identity->role = self::LOGGEDIN_ROLE; $result = Zend_Auth_Result::SUCCESS; } else { $identity->role = Zend_Amf_Constants::GUEST_ROLE; } return new Zend_Auth_Result($result, $identity); } } |
The Auth class extends Zend_Amf_Auth_Abstract because Flex seems to require username and passwords as being the only mechanism for passing credentials. The abstract class defines a method that hooks in with the special commands and passes the special credentials to the special adapter. Clearly your authentication mechanism should be better than the one that I put in here, but you’ll get the idea. The most important part is adding the role property to the identity object and passing it to the Zend_Auth_Result object.
Then in your gateway you need to add this adapter as well as create an simple ACL.
1 2 3 4 5 6 7 8 9 10 11 12 | $server = new Zend_Amf_Server(); $server->addDirectory(realpath(__DIR__.'/../services')); $acl = new Zend_Acl(); $acl->addRole(Auth::LOGGEDIN_ROLE); $acl->allow(Auth::LOGGEDIN_ROLE); $server->setAcl($acl); $auth = new Auth(); $server->setAuth($auth); echo $server->handle(); |
This adds the new Auth role to the ACL and says that it has access to everything. Since there is no place where I allow guest access (denoted by Zend_Amf_Constants::GUEST_ROLE in the adapter) guest requests will be denied.
With just this little bit of code you now have a mechanism that will provide restricted access to all of your service objects.
Comments
Kevin Schroeder’s Blog: Authentication Using Zend_Amf
[…] to his blog today showing how you can use the Zend_Amf component in your Flex+PHP application to authenticate users with the same authentication structure the rest of the application uses. I forget why, but a few […]
Cubny
with Flex it’s quite simple and straightforward as you showed, but when it comes to amf in Flash, zend amf authentication has a big issue,
look at this:
http://framework.zend.com/issues/browse/ZF-9985
most of complex games do not use flex for performance reasons.
Kevin Schroeder
That is actually what this code “fixes”. The authentication in Zend_Amf_Server should use at least a basic ACL and then the issue on that link is rendered moot.
Authentication using Zend_Amf – HackIX: Small Hacks for a Large World
[…] via Kevin Schroeder’s blog – Zend Technologies. […]
salman
hi, Kevin you have been doing great job.I have seen your other tutorials regarding Zend amf and they are really helpful. it would be really great if you could also make a tutorial on Adobe flex authentication using zend Amf,zend Auth and zend Acl where the project on server side should be zend framework (MVC based) and also what to do on Flex side. Kindly keep it to the real world scenario and from start to finish because there are a few articles on web but non of them any closer to real world , while some are bad structured and incomplete other make further confusion .I would greatly appreciate your response regrading this.
Thanks
Kevin Schroeder
Nothing like a simple request 🙂
salman
Thanks Kevin, I know it’s difficult but not for you.Hopefully you gona come with something really great.By the way thanks again for reply on such short notice.
Kevin Haferkamp
Hi!
Very good tutorial, but I have one question:
When I set wrong credentials on the RemoteObject I get this error “Channel.Ping.Failed error Authentication failed: url: ‘http://localhost/projects/zendAMFTest/public/gateway.php'”
Is it possible to return a more helpful error response like “Access denied”?
Or do I have to catch this error in my fault event handler? Or does the fault event handler just get called when a php error occurs?
Then I have another question: Your method requires to send and validate the credentials each request? Or am I wrong?
For example I have to services, ServiceA and ServiceB.
The acces is now restricted to the loggedin-Role.
Now I would like to have a simple login view and after a successfull login the user could use the Services ServiceA and ServiceB.
How can I implement this?
Because now every RemoteObject (one for ServiceA and one for ServiceB) use the setCredential function and this leads to the point that on every service call the credentials will be checked against the database.
This could produce a heavy overload, or did I miss anything?
My dream scenario is the following:
Login View (visible as role “guest”)
-> calls ServiceA login method
change state to the view in reference to the role (like admin and user role)
-> Admin view can now use the Services ServiceA and ServiceB to do some cool stuff with the backend. (Like ServiceA->getAllEmployes()).
Would be very nice to get to this point, but I am puzzled a bit 🙂
Greetings,
Kevin Haferkamp
salman
hi Haferkamp, you have asked great question regarding zend amf and i totally agree with you and matter of fact is this issue regarding zend amf authentication is affecting a lot of developers out there. in fact i have searched every single blog on internet for past Eleven months are so regarding this but found nothing satisfaction. Hopefully if you found some thing Please also let me know. i would greatly appreciate your affort regarding this.
sincerely salman .
salman
hi Kevin, Can we use zend_acl with zend_amf with out involving
zend_auth at this stage (e.g $server->setAuth($auth)) and authenticating the user through some where in controller(MVC) through http post and then get athenticated person role and use it in zend_Acl ($server->setAcl($auth)) for zend Amf . if yes then how? and if Not then why?
Another thing is can we declare all zend Acl rules in some specific place (like in a class) and then use it where ever we need it so that we do not have to declare it again and again in different places with zend amf server declaration (as done by Forrest Lyman in his book Pro Zend Framework Techniques Build a Full CMS Project) .if yes then kindly describe.
will greatly appreciate your reply regarding both of these questions
sincerely Salman
Kevin Schroeder
You can add the auth adapter at any point in your application provided it’s before the service handler dispatches its request.
You can put the access rules any place you like. But, in general, you should b putting them in the service classes in the method initAcl(). The thing about ACLs is that the logic implementing them can get quite complicated very quickly and so having “conditional” ACLs that only load up the part of the ACL that will be needed for a given service request will probably make your life easier.
salman
Thanks a lot Kevin, i implemented it and it’s work great bro.
again thank you very much.
sincerely salman