2

I have an Angular 4 SPA (single page application) being served by a server that has ColdFusion 11 on it. I'm using, via AJAX calls, many functions contained in .CFC files on that ColdFusion server.

I want the following to happen:

The user goes to my Angular 4 app's page (myapp.mydomain.com) and will be redirected to the login screen (myapp.mydomain.com/login) wherein they will enter their username and password. The Angular 4 app will then call a .CFC on the server to validate their login info. The .CFC will return a "yes" or "no" validating the info. Then the Angular 4 app redirects them to myapp.mydomain.com/home (or wherever I want them to go).

At the same time, I want ColdFusion to create a new session for this user -- so that, if the session times out, or the user logs off, any further calls to any other .CFCs will be rejected.

AND if the ColdFusion session times out, I also want the Angular 4 app to notice this and redirect the user to the /login route.

Basically I need to secure both the client-side (using an Auth-Guard-style service in Angular 4, which I know how to do) and the server-side (using ColdFusion 11 Session Management, which I do not know how to do), and I need them to communicate constantly about the authorization status of both, without having to ask every single time whether or not the session is still valid. (Can the Angular 4 app somehow read the ColdFusion session cookies?)

How do I get these two things to cooperate with each other like that? Or is my ignorance of ColdFusion session-management blinding me to a far better solution that I haven't thought of yet?

Any suggestions are appreciated. Thanks!

  • I know more about the coldfusion side than the angular side (as in, I've never even looked at angular). Trying to understand the problem though... all of your ajax requests are to the coldfusion server, right? So you begin every remote-accessible cfc function with `if(!IsDefined("session.user")) return { some object that your client will recognize as a session timeout };` and when the client gets that object as a response, it sets its own internal state to "unauthenticated". Why doesn't this work? –  Jul 13 '17 at 23:39
  • Ok -- that makes sense. But how do I get "session.user" to be defined? I'm very unsure how to make ColdFusion create a session when I'm not using any .CFM files... I honestly don't even know if ColdFusion *can* do that? The documentation for Application.cfc is very unclear on that front. And doesn't ColdFusion create cookies in the browser when it creates a session? Wouldn't I need to manipulate those, sending the token(s) stored therein back-and-forth? How can I get my Angular 4 app to access ColdFusion cookies? Or do I even need to have it do that? Thanks for your response. – Laurence MacNeill Jul 14 '17 at 00:37
  • I agree the documentation leaves a lot of things unclear. I'll try to map out the basic flow of session management in an answer... –  Jul 14 '17 at 00:54

1 Answers1

2

On the server, cfc's are not exempt from automatic session creation and cookie management

For a request to have access to session variables, these conditions must be met:

  • The client must make a request that gets routed to ColdFusion (i.e. it hits a cfc or cfm, not some static html or js).
  • There must be an Application.cfc in the same directory or some ancestor directory of the one where the requested cfm/cfc is.
  • The Application.cfc must enable session variables with this.sessionmanagement = true;

When those conditions are met, ColdFusion will associate the request with a session. There are 3 ways this association can me made:

  • The client already has valid session cookies and sends them in the request. Your CFML code can read session variables that were created in previous requests, and set new values for future requests to read.
  • The client is new, and has no cookies. ColdFusion creates a new set of cookies and a new session scope. Your CFML code can set session variables for future requests to read. The new cookies are automatically sent to the client along with your response.
  • The client sends cookies, but they correspond to an expired session. This is handled just like the previous case. New cookies are sent and an empty session scope exists for your CFML to fill.

On the client, ajax requests are not exempt from cookies either

The underlying XMLHttpRequest gets and sets cookies from the same cookie store as all other requests. If the requested URL matches the domain, path, secure flag of a cookie, XMLHttpRequest will send the cookie. And if it gets valid cookies in response, it will add them.

Mostly you just use session variables without thinking about cookies or how they got there

So for your use case, if your login page is internally routed to login.cfm, and there's an Application.cfc nearby, the session scope is ready for you to use as soon as login.cfm starts. You can do

if(IsDefined("form.username") && IsDefined("form.password")) {
  if(...check password [aka the hard part]...) {
    session.user = form.username;
    location(url="/home");
  } else {
    location(url="/login");
  }
} else {
  ...print the login form...
}

And your logout code can StructDelete(session, "user")

Everywhere else, in all your cfc's and cfm's, the question of whether the request came from a logged-in user is simple: if the client has previously logged in, and the session hasn't expired, then session.user exists. Otherwise it doesn't (you will have a session - there is always a session because ColdFusion creates one before running your CFML code - but there will be no user variable in it until you put one there).

You can set other user-related variables in the login request too (and unset them at logout), like real name, preferences, anything you want to load from a database that will be frequently used and infrequently updated, you can keep in the session scope. Also there's cflogin which is supposed to help with managing user logins, but it seems pretty unnecessary. (See Why don't people use <CFLOGIN>?)

Your desire to avoid "having to ask every single time" is not really fulfilled, but the "asking" is minimal. The client sends the cookies in every ajax request, which is effectively "asking" for the session to be continued. And it must check every ajax response for the "session timeout" error. And on the server, every request-processing function must begin with a check for existence of a session variable.

But you can use an ajax wrapper on the client to ease the pain.

On the server, you can use an onRequestStart to provide a common "precheck" to all requests so you don't even need to have if(...no user...) { return "OH NO"; } at the top of every function.

  • It appears that calling a function inside a "login.cfc" file will work the same as using a "login.cfm" file to serve up a login page, yes? So it looks like everything you wrote above should work for me. I can just put the user's name into the "session.user" variable inside my login function inside my login.cfm file, and it will be stored in the session scope for every subsequent call to any CFC located inside the folder structure that contains my Application.cfc file. Thanks so much for figuring all that out! I was at my wits' end! Been banging my head against the wall for two weeks now. :-) – Laurence MacNeill Jul 14 '17 at 16:15