views:

111

answers:

4

Dear Abby,

I wrote a RESTful servlet, and the UI developer wants to save the logged-in state on the server.

He made this strange claim: "I haven't encountered a production REST implementation that is pure REST. The ones I've seen all have had the server maintain a session."

I find this difficult to accept. First there's the technicality that there are plenty of plain HTTP pages out there, all purely RESTful. Second, yes, there are non-RESTful implementations labeled RESTful, just like there's brass labeled "gold". Third, just because everyone else jumps off a bridge doesn't mean I should.

Background: It's a JavaScript Ajax web application using HTTPS and Basic authentication. To avoid the usual (uncustomizeable) browser login popup box, the application shows a login screen with the product logo and text boxes for name and password. The name and password are stored in the document and sent in the Authorization header for each request. If you refresh the page, the name and password are lost and the user must type them in again. That's considered a bug; the UI developer wants to be able to press the refresh button without giving the password again.

So the developer wants to use a cookie or a JSP session. Abby, is it true that in the end every REST implementation maintains application state on the server? Or is there a way I can solve this problem and still maintain my RESTful purity?

+1  A: 

I see no problem with using a cookie to maintain this information on the client side. Cookies only become a problem when they are used as pointers to some server side session state.

The main thing you have to be concerned about is the security of the information in the cookie. You probably don't want to put the clear text password in the cookie :-)

Darrel Miller
One technique is to use a typical session cookie and store the authentication on the server side. This works well for ASP.NET, so I expect it should be a reasonable approach for similar platforms.
Steven Sudit
Darrel's idea could work. HTTPS, so cookie can't be seen in transit. Would require some configuration / modification of JAAS on the server side (JBoss) to look in the cookie if the Authorization header is not found.
Mark Lutton
Ok, but you still don't want passwords and such in a cookie if you can avoid it. What the client doesn't retain, it can't leak.
Steven Sudit
What many REST apis do is return some kind of token from the login process. This token can be stored in a cookie and then sent in the authorization header on future requests. This token does not need to contain user information as long as the server has some way of verifying it. It can also expire after a period of time.
Darrel Miller
I'm not sure how this differs from a session ID.
Steven Sudit
An authentication token has a single purpose. To identify the requester. A session ID tends to be a pointer to some state stored on the server.
Darrel Miller
As explained in the commentary around my answer, what's stored in the server for that session is definitely data, but it doesn't have to be state.
Steven Sudit
A: 
  • By default browsers don't ask for crendentials again when you refresh a page. You may want to look into this, because if you fix this then your problem is solved.
  • You could maintain a REST service for the most part, except that the user can authenticate either with HTTP or with a cookie. I.e. the service also works if you do not have the cookie.

Regards,

Abby.

Sjoerd
The browser only asks for (and retains) credentials when it gets a 401 Unauthorized response, and then it puts up its own login box which we want to avoid. (We can't avoid it if you type in your password wrong in the first place because we can't intercept that 401 response. So when you type your password wrong you DO get the browser login box, and then after that the browser DOES remember your credentials. (And primes itself to be a CSRF patsy but that's another story.))
Mark Lutton
@Mark: In forms-based security, the login page itself is usually unsecured so that it allows anonymous authentication and does not generate 401 responses. Are you doing something different here?
Steven Sudit
@Steven: the web page is unsecured (except for HTTPS) and makes XMLHttpRequest calls to the RESTful Servlet.
Mark Lutton
@Mark: Interesting. if so, then where is the 401 coming from? The servlet?
Steven Sudit
The JBoss server is configured for Basic authentication.
Mark Lutton
A: 

If you return a session ID of some sort after authentication and have it passed in with each call, this really isn't much different than passing the authentication credentials with each call. In terms of performance, the latter can still cache the credentials, so there's no performance hit. In terms of security, not having to keep the credentials around is better, since sessions expire but credentials (usually) do not. Arguably, avoiding sessions is more robust, as the server can forget everything between calls but still work.

In short, there's no overwhelmingly strong argument in either direction, and you probably shouldn't get hung up on whether you're being sufficiently "pure" in your commitment to REST. The real issue is the behavior of the client, and that's something you can tweak independently of this, as Sjoerd suggests.

Steven Sudit
There is a big difference in that once you open that door to storing client state on the server it becomes the easy route for storing all sorts of stuff. What started out as a tiny bit of session state, is now causing scaling problems and making your clients dependent on one particular application server. It's a slippery slope.
Darrel Miller
Depends on whether it's data or state. For example, I wrote a system which generates a security profile upon login and then caches it. This data is not state because it can be recreated as needed, though at a price. In short, if it has to go in a session, it's state, but if it can be tossed into a cache, it's not.
Steven Sudit
@Steven I'm not familiar with that distinction between data and state. I prefer to distinguish between client application state and resource state. Resource state you can give an URI and anyone should get the same thing if they dereference it. Client application state is specific to one client.
Darrel Miller
@Darrel: State needs to be avoided in the middle tiers in order to allow scaling, but failure to cache non-state data in those tiers kills performance. Whether it's per-client or not does not change anything. Therefore, it is a good practice to use a session to store non-state data.
Steven Sudit
@Steven, I think I am starting to understand your perspective. What you call data, the REST crowd call Resource state and what you are calling State, is referred to as client state, or application state. The one difference that I think I am seeing is you sometimes store "Data" in your server session. REST requires all "data/resource state" to have a URI so for example, your "security profile" would need to have an URL associated to it. e.g. `/User/25/SecurityProfile` In theory, another user, if they had sufficient privileges would be able to dereference that URL.
Darrel Miller
@Darrel: I hate to get caught up on terminology, but I'd also hate for us not to understand each other. State is information that a tier has that cannot be automatically obtained from another tier. The right place for state to end up in an n-tier architecture is on the database. If it lingers in the middle, such as an applications or web server, it prevents scaling out because a call to a different server will not yield the same results. So a good server passes state through but does not hoard it. Whether the data is per-user or per-app is irrelevant.
Steven Sudit
@Darrel: Let me try to address your examples directly. The security profile I spoke of is not state: it can be regenerated from the database by any middle-tier server given only the user ID. It is cached as needed, perhaps in the session, perhaps elsewhere, but if it gets lost, no harm is done. Therefore, there is no need for it to have an associated URL. Arguably, you might want the app server to serve up the security profile for use by the web server, so it would get a URL. Whether this should allow restful writes, though, is another matter. Does that help?
Steven Sudit
+2  A: 

I think that for practical reasons (mainly browsable abilities) you need to distinguish between application state and authentication state. I can't think of any authentication mechanism that doesn't retain some form of state on the server side.

What really matters is how decoupled it is from the application. For example, HTTP Digest retains some form of state on the server, but this is clearly abstracted away as part of the normal WWW-Authenticate and Authorization header negotiation. Because most browsers support it natively, this is orthogonal to the application and as such doesn't break the statelessness principle of REST.

Nowadays, because users have some aesthetic expectations that HTTP Basic/Digest authentication don't meet in the browsers, websites tend to use form-based authentication and subsequently cookies. To be fair it's more than just the way it looks, it also a matter of usability (e.g. "forgotten your password" information, although that could be in the body of a 401 response) and security. Browsers don't let you log off from Basic/Digest/Certificate authentication easily, unless it's done entirely in Ajax within a single page, as you've mentioned, and that can help CSRFs.

I think cookies are acceptable for authentication, but make sure you don't store application-related variables in the session.

You can read some of Roy Fielding's comments on the topic:

Authentication is orthogonal. Cookies are also orthogonal when they are simply used for content negotiation or authentication. However, Cookie authentication is not allowed in REST because it lacks visibility, which causes security problems because the other components don't know it is sensitive information.

EDIT (further comments on the security aspects):

I realise Roy Fielding's comments in the message I've quoted is against cookies for security reasons. He's right of course. However, in my opinion, it's harder to protect against CSRF via Basic/Digest/Cert (which wasn't really on the radar in 2003, date of that message) than against cookie theft. It depends on the implementation of course. There isn't a perfect solution, but if you use cookies, use secure cookies, over HTTPS.

Bruno
I should have said "any *secure* authentication mechanism" in the 1st paragraph.
Bruno
Bruno, thank you for making this distinction clear.
Steven Sudit
Bruno, in response to what you added, it's possible to have it both ways. The document could contain the ID of the user, but cookies could be used to decide whether to reject the document as inauthentic instead of acting on it.
Steven Sudit