deitch / cansecurity

nodejs/expressjs authentication and security library
MIT License
150 stars 53 forks source link

cansec.clear doesn't seem to work #7

Closed socketwiz closed 9 years ago

socketwiz commented 9 years ago

Either cansec.clear isn't working or I'm not understanding what it is supposed to do. I'm using cansec with restify: https://github.com/mcavage/node-restify not express and for the most part its working great. What I really like about cansec is step number 2 in your docs about "how authentication works":

Was there an X-CS-Auth header? If so, validate using the auth header.

So I authenticate once with a username/password, then I use the X-CS-Auth header that is returned to authenticate the remaining requests. This works very well. It will even timeout after the default of 15 minutes and I'll need to re-authenticate with the username/password. However, now I'm trying to setup a logout action. From the docs I see:

app.get("/logout",function(req, res){
    cansec.clear(req, res);
    res.send(200);
});

I've implemented this and it runs without error, but after I run it, I was expecting my X-CS-Auth requests to fail and require a new login with username/password...however it does not. Is this a misunderstanding on my part, am I doing something wrong, or is this possibly a bug?

deitch commented 9 years ago

Restify is a great idea; I should add it to the tests for cansecurity, and for booster https://github.com/deitch/booster for that matter. Since both follow the (req, res, next) convention, there is no reason both shouldn't work, but the tests are pretty expansive; should be sure.

Thanks for suggesting that. Now on to your question ...

deitch commented 9 years ago

OK, I see what the question is. It could be a feature or a bug, depending on your perspective. That perspective (yours) will help here.

One of the goals of cansecurity is to provide single-sign-on (SSO) across servers. So rather than X-CS-Auth being just a session token for this server, it is a cryptographic signature that says, "this is validated user X", completely independent of the server.

It means you can have 3 servers, do the initial user/pass authentication against server A, have the next request go to server B using the X-CS-Auth token generated by A, and it will treat you as logged in. It will do it without any shared backend; most such systems require some kind of shared database or service to validate the user token.

However, it also means that "logging out" on the server can remove the session on this server, etc., but it cannot revoke the token, because it isn't something stored in a server-side list (unlike the session, which is stored).

I have been wrangling with this part of it for quite some time, which is why this is the one area where the tests are sparse. Here are some possibilities with pros and cons. Share some thoughts:

  1. Leave as is: the X-CS-Auth is a token that indicates validity across distributed possibly non-communicating servers, so until the client-side invalidates it, not much to do. Just make sure your client clears the token locally. This is what I have done in my apps until now.
  2. Create a "non-SSO" option: There will be 2 kinds of X-CS-Auth headers. SSO is like the current, non-SSO is one that is valid only on this server. Thus, it can be invalidated.
  3. Invalidate locally: this would require maintaining a revocation list, but it isn't too bad. So even if you present a X-CS-Auth token, if this server revoked it, it will be worthless on this server. Of course other servers will not see that revocation list, so they will accept the token.
  4. When doing cs.clear(), instead of just removing the header, send back a specific new header but one whose expiry date is, say, 10 seconds ago (or 10 years, or the epoch, or whatever) or something like that. Of course, a client or browser could still have saved the previous one and reuse it (as long as it is still within the expiry window), because of the SSO.

Thoughts?

socketwiz commented 9 years ago

I had not actually considered the cross server implications. Now that you have described how its really supposed to work, I think I am in favor of option 1. I can just invalidate on the client side as well.