If you don't know what I'm talking about, check this post. Essentially - I'm trying to wrap my brain around how to best get Flex 2 talking to ColdFusion in a secure manner. My earlier posts showed how to graphically start up with a login screen, require you to login, and then switch to the main view.
Today I've actually hooked up my Flex code to a real CFC. Let's look at how I did that. The first new lines to my Flex code are:
<mx:RemoteObject id="core" destination="ColdFusion" source="demos.flexsec3.core" showBusyCursor="true" >
<mx:method name="authenticate" fault="alertMsg(event.fault.toString())" result="checkAuthResult(event)" />
</mx:RemoteObject>
This creates an object named "core" that represents my ColdFusion Component. Notice the "source" attribute is the "dot" path, from web root, to the CFC. (More on that later.) I have only one method defined, authenticate, and I've set up both a fault handler and a result handler.
The fault handler simply dumps the error, so lets look at checkAuthResult:
private function checkAuthResult(event):void {
var result = event.result;
if(result == 'false') {
Alert.show("Authentication failed", "Errors", mx.controls.Alert.OK);
} else {
mainView.selectedChild = mainStage;
}
}
My CFC, which I'll show in a second, will return either true or false. I check the contents of this variable, and depending on the result, either show an error or hide the login stage.
Prety simple, right? The CFC doesn't do much yet. It's authenticate method simply has this:
<cfif arguments.username is "admin" and arguments.password is "dharma">
<cfreturn true>
<cfelse>
<cfreturn false>
</cfif>
Since this is only a demo I'm not going to worry about hooking it up to a database.
So - let me review what I've done: I've defined a CFC service in my Flex code named core. (Not a very descriptive name, but...) I defined a method on this CFC and what Flex should do on error and on the result. I then check the result and either tell the user he didn't login correctly or go ahead and show the main application.
My questions/problems are:
- It seems like the source attribute must be hard coded. This has always been a pain in the butt for me (well, "always" for the few Flex 2 applications I've built) as it means I have to change it from source to production. Obviously I could have set up things differently, but I wish I could abstract that value out - perhaps into Flash Vars. Is that possible?
- I'm not storing the username and password. As I have no idea (yet!) how I'm going to talk securely to the CFC backend, I don't know if I need to. I assume I will - but for now I don't both storing the values.
- As I mentioned, the fault handler should be more intelligent. Any application based on back end services like this should have some nice error handling.
If you want to view this demo, please go here:
http://ray.camdenfamily.com/demos/flexsec3/SimpleRemotingTest.html
As before - please feel free to point anything I did wrong.
Archived Comments
Just like in CF there are many ways to go after a particular problem. You are correct that you could use flashvars to set up the path to the CFC in question. In this case though, I think you will need to create your remote object in AS. The easiest way for this to happen is to add a creationComplete event handler to your application and have it call a function. Inside that function you could set the source property for your remote object.
As for storing the returned state of the user, you could do one of several things. The most simple would be to have a property on your application that holds the value. The first change I would change there however is updating that from a simple value to some kind of user object so I can add methods like isAuthenticated() or getAccessLevel(). Your authentication call could actually return a cfc which contains the appropriate values to initialize this local as object. Once the application starts to grow, I recommend using something like a ModelLocator to store those values that are needed across and entire application.
I wouldn't say you did anything wrong, but here's a suggestion. I like to use inline conditionals to dynamically enable buttons like this. Since you're checking that the fields are both not blank before submitting, the following would prevent the submit button from even being clicked until there was at least one char in each field.
<mx:Button id="loginButton" label="Login"
enabled="{(username.text.length == 0 || password.text.length == 0) ? false : true}"
toolTip="{loginButton.enabled == true ? 'Click to submit' : 'Enter username and password'}" click="checkForm()" />
Regarding the source of the remote object, wouldn't the use of mappings help?
todd: Mappings - well - in my case, my dev is very different from my live. It probably isn't a good idea to do that - BUT, in general, I can see something like that as being a setting. So for example, I'd like all my RemoteObjects to use a source of #root#.foo. Get my meaning?
todd: That button looks insane cool! :) I'm not sure I want to use that - but I like it. I'll play with it.
I'm just brainstorming here, but how about an application var for your path to the cfcs being dynamically set in an application.cfc (or cfm) depending on dev or prod - and that value then being written out to an xml file on the server. Then on load of the Flex app, you load the value from the external xml file and use that as the remoteObject path? This may require the Flex wrapper to be cfm (instead of html) to invoke the application.cfc.
I hesitate in using Flash vars (simply because I'm naive to them). I'm also interested to see what others come up with.
Ray and all:
A security question I have is how to ensure that only my Flex application can call the CFC methods. Since I have to set access="remote", the CFC method can be called via the URL.
Ray and All:
Regarding my question above, I think I've figured out a way to create a login and logout process and secure the CFC methods so that only my Flex application can call them.
See: http://www.brucephillips.na...
(you can right click to view the source). The correct username is admin and the correct password is dharma.
I've modified the code Ray had in his latest example and have also incorporated some code I found in the Using ColdFusion MX With Flex 2 Adobe PDF documentation. There is a section in that document that discusses Flash Remoting Update and Authentication.
Basically, I have the Flex application set the remote credentials to the username and password entered by the user (see http://livedocs.macromedia.... and its discussion on method setRemoteCredentials. The login method in my CFC uses CFLogin to process the user's provided information that is now in the CFLogin scope. If the correct information is provide the roles attribute for cfloginuser is set to flexadmin.
My getData CFC function uses the roles attribute set to flexadmin. Thus, unless the user has successfully logged in he cannot call this method from flex (or anywhere else such as in the URL).
I also provide a logout capability on the "secure" view in the Flex app, which will call a logout function in the CFC.
Let me know what you think about this variation.
Bruce
Bruce, you only need to set access=remote if you are using flex with web services. If you are usimg AMF (Flash Remoting) you don't need to set access=remote.
Ray - appreciate what you're doing with this. I've built some apps with Flex and security has been one of my biggest questions as well.
Btw - in the last version of CF, they added isSoapRequest. You can use this to allow Flash Remoting calls but NOT Web Service calls. Or vice-versa.
Ray - all of my calls via Flash Remoting are done with access=public - if you are using Flash Remoting you don't have to set access=remote.
Kevin:
Thanks for the tip about using access="public" instead of access="remote". I did not know you could do that, but tested the demo I mention above and access="public" worked fine.
I still like the idea of using setRemoteCredentials in the RemoteObject call to a CFC function that uses CFLogin. You can then user role based security.
I didn't know that either. Thanks!
I've been using the facade approach to remote object calls. Have the Flex app talk to a facade which then talks direct to the CFCs. The facade gets access="remote" the others access="public" or package. Your facade can live under your web apps root with all the CFCs living somewhere else not web accessible. Note you may need to tweak your flex config file under your web-inf folder concerning accessiing remote objects and mappings.
IIRC, use of setCredentials() requires cflogin, something I'm no fan of.
regarding the access=public thing, you can do this as long as your flex config on your cf server is set to allow this. This makes the security much less of an issue.
There are also settings in there to return structure keys as all lowercase instead of uppercase and a few other things.
Also, have you thought any about requiring a "key" of sorts to be able to use the coldfusion methods? anything calling must provide a key to be able to use the service, and then your flex code just passes that through when calling? May take a little work, but should make it easier for you to be sure its only you calling the code. A lot of commercial webservices use things like this.
I am in the same dilemma about moving from development to production. I was going to look next on how to do remote object calls using actionscript only instead of the mxml tags, to see if I can construct the object on the fly instead of hardcoding.