From stateful to stateless RESTful security using Spring and JWTs – Part 1 (intro)

The benefits of stateless authentication are many.

Imagine that your little web app suddenly becomes a worldwide hit. Now thousands… no, hundreds of thousands of persons are accessing it simultaneously.
Your little virtual server won’t cope with all that traffic, but it’s okay because you can add servers and distribute the traffic around. Just put a load balancer to make sure nothing overloads, right?

Ah, except that if your users are identified by server-side sessions (as it is the case with stateful, session-based authentication) then their requests must always be redirected to the same server they were authenticated on, because that session info is not shared by all servers in your cluster.

No problem, I’ll just tell our load balancer to do sticky sessions.

Okay, but if your web application is a true hit you will soon notice that it is not optimal for a whole lot of reasons: the load is not being balanced evenly, there’s an impact on the servers memory…
Imagine that one of your servers in the cluster goes down: oops, a bunch of users just got disconnected. And boy, they’re not happy…
To be totally fair, sticky sessions can actually be a good fit in many cases. It is relatively easy to set up and does not require any modifications on the application side. If your traffic is moderate, you should be good. But wouldn’t it be great if sessions didn’t need to be stored on the server?

If credentials were not stored on the server, then no matter where the load balancer would direct traffic the user would still remain authenticated. If a server went down, the user’s requests would just be redirected to a working machine without any interruption. As soon as traffic spikes would be detected you would be able to add some virtual machines and voilà: the overload issue is resolved. Finally, although session objects do not necessarily take a lot of space (especially when using them strictly for authentication), we would not need the server to save some precious memory to store these session objects.

How cool would that be!

Okay I’m sold, how can I make my authentication stateless?

The most obvious idea would be to send our session object’s content back to the client and have them stored in, say, a cookie. They would of course be encrypted by the server before, so that no prying eyes would be able to read them.
When querying the server, the client would send that encrypted cookie along with the intended request. The server would then decrypt the cookie’s content and consequently verify the obtained credentials. Since the server had previously encrypted the cookie’s content using its own secret key, we are pretty sure that the credentials obtained are authentic… unless the secret key is leaked, that is.

This approach has been made pretty much straightforward thanks to contributions like this one, which allows you to intercept the session’s content, encrypt it and send it to the browser as a cookie. Essentially, you should be able to add something such as “HTTP Stateless Session” to your project and happily keep using all the cool features provided by Spring Security or similar frameworks.

JSON Web Tokens

JWT (JSON Web Tokens) is the open standard version of that concept: a JSON-based solution for creating secure tokens that contain a certain amount of claims, such as “I am indeed logged”, “I have administration rights” or “I have small hands and a sorry-looking orange rag on my head”. These tokens are standard, compact, encrypted and signed by the emitting entity so that whoever receives them can ensure they are legitimate.

As such, they can be perfectly used for authenticating a web or mobile application to a server exposing a RESTful API.

One of the major advantages of JWT compared to session content’s serialization is that it can be used by any language or technology that can handle JSON, including Java, .NET, JavaScript, Python… Just point your browser to https://jwt.io/ and have a look at the amount and variety of libraries available to generate, sign and verify JWT tokens! Neat, right?

Table of contents

So this is my proposal: we are going to revisit authentication for Spring-based RESTful APIs from a stateless perspective. We’ll proceed step by step:

  1. First we’ll go for a Basic, stateful authentication scenario using Spring Boot and Spring Security: we’ll set up a project and aim at the known “basics”, while setting up the testing environment to make sure our security implementation actually works.
  2. Token-based authentication will gradually replace sessions as the holders of authentication credentials. We will take our first steps towards statelessness.
  3. We will then bring JWTs to the equation and end up with a fully-usable solution of stateless authentication for RESTful APIs.
  4. Finally, we will examine stateless CSRF protection for that extra layer of protection.

We’ll also discuss whether stateless security is the best option, and I’ll make sure to provide you a list of reference material to further study the topic.

At the end, we should have a good starting point for securing our RESTful projects in a stateless fashion.

So next time we’ll start by setting up our initial project. Until then…

Cheers!

7 comments

  1. Ninigi says:

    Hi!

    First of all, thank you for writing this awesome blog. Currently writing my thesis I learn from it more than from numerous books and lectures. Thank you!

    Secondly, I have a question about a post you wrote some time ago. https://www.codesandnotes.be/2014/10/31/restful-authentication-using-spring-security-on-spring-boot-and-jquery-as-a-web-client/ Sorry for not commenting under that post, but comments there are closed. In it you wrote that after a successful login by default ‘/login’ endpoint we should be redirected to the original source which we wanted to access. I’m using spring security for log-in and receive 302 on endpoint I want to access (eg.’/books/all’) because it’s secured. Then I’m redirected to (‘/login) and receive GET with 200. And then nothing happens. I have no idea why since I need to be redirected back to ‘/books/all’. Any idea? I’m using spring, spring security, react and isomorphic-fetch for the call.

    Thank you in advance for any help.

    • Hello Ninigi,

      Thanks for the kudos. I’m glad to hear my blog is helping other developers!
      About comments, I indeed had them closed after a year or so: answering questions on very old material is sometimes tricky… But you know what, I’m going to reopen all comments and see if I can handle the flow 😉

      As for your question, to recap: when working with JSPs Spring Security can control the redirection to a login page and, after authenticating, the redirection to the page that triggered the authentication request. When going RESTful, the front end only gets simple answers (in this case, a 401 or 200) back from the server. So it’s up to the front end to do the redirection job!
      In the tutorial code, I stored the “pre-login URL” in a cookie and retrieved that pre-login URL” from that cookie after the user authenticates. This is what makes the post-login redirection work.

      But I understand you are working with React for the front end. Now, I have absolutely no experience with React (I’m mostly working on Angular), however you say that you have a “GET 200”. I would expect that GET to be something like the answer to the request that fetches the “login” page, but not the answer to the login submission: given the tutorial’s Spring Security configuration on the server side, the login credentials should be submitted as a POST. So I would have expected a POST 200.

      So my first question is: can you check with a browser debugger whether you actually submit the credentials? Are they submitted as a POST?
      If after submitting your credentials you manually browse to “/books/all”, are you still redirected to the login page? If yes it can only mean that Spring Security is not doing the authentication…

      One thing you can check: the credentials must be posted NOT as a JSON structure of any kind, but as a content type “application/x-www-form-urlencoded”. This is implied in the jQuery code in the blog post, which essentially simulates the “submit” action of a simple HTML form (which submits data as a form). But when using frameworks such as Angular or React, that probably needs to be specified explicitly. Check this post to see how I’ve prepared that login POST in Angular: https://www.codesandnotes.be/2015/09/04/angularjs-web-apps-for-spring-based-rest-services-security-the-client-side/

      Now try and check with the browser’s debugger whether your application is doing a POST of your credentials in x-www-form-urlencoded, and make sure you get an OK 200 response from that POST. If that is indeed the case and you are indeed logged (that is, you can access “/books/all” by typing the address manually) but you are not automatically redirected after logging in, then I suspect the problem lies in the React code.

      Hope this helps!

      • Ninigi says:

        Hello,

        thank you very much for your response! It did help me! As it turned out first of all I didn’t know that JSON isn’t supported ( All React POSTing tutorials were using JSON and I would have never guessed that this was one of the issues). The second thing I was doing wrong was expecting back-end to handle redirect. Somehow I missed it in the post I enclosed and I am sorry for that.

        Unfortunately it’s still not working. After making these changes I went back to your post and implemented RESTAuthenticationEntryPoint, RESTAuthenticationFailureHandler and RESTAuthenticationSuccessHandler. Also I did some changes in React. Since you mentioned not being familiar with it, please let me write it using some pseudo-code.

        1. Make GET request at http://localhost:8080/rest/book/anna/all
        2. If success contnue, if error make POST request at 'http://localhost:8080/login', 'username=c&password=d'
        3. If POST successful make GET request at 'http://localhost:8080/rest/book/anna/all'.

        Now in the Network tab I can see that firstly I receive 401 GET to /rest/book/anna/all endpoint. Then POST ‘200’ on /login. So far everything works as expected. But then I make a second call to /rest/book/anna/all and receive another 401 GET. Also I no longer see JSESSIONID cookie in Application tab and instead I receive __cfduid cookie.

        I understand that the issue lies in the fact that somehow backend doesn’t remember me as a logged in user. I did everything like in the tutorial and on the front-end I’m not yet using cookies but giving URLs by hand. I’m using axios library (just get and post).

        Do you have any suggestions why it may still be failing?

        Once again thank you for your help!

        • Hmm, is that “cfduid” as in “CloudFlare” 🙂

          It’s always tricky to debug without seeing the project, but here’s my hunch:
          1) You get a 200 after doing the login POST, so that normally means you have been correctly authenticated on the server
          2) When looking at the login POST response, you should see that it returns a cookie with the JSESSIONID. Is it there? If yes, then you are authenticated.
          3) However , doing your GET on /rest/book/anna/all will only work if the browser is sending the JSESSIONID cookie back with that GET request. Otherwise the back end won’t recognize your browser session and, most importantly, the fact that you are authenticated!

          Based on what you describe I would therefore say that the problem is not on the back end, but rather on the front end. I’m not quite sure what you mean when you say that you’re “giving the URLs by hand” and “not yet using cookies”, but you should try and find out why that JSESSIONID cookie disappears, and make sure that cookie is sent with your GET request.

          I hope this will help you in finding where the issue is!

    • Hi there, JWTs are indeed the new trend, even though many are still using stateful authentication (Spring Security’s reference manual used to vigorously defend the session-based approach).
      As a matter of fact, this post is the introduction of a series of posts to help readers go from stateful (session-based) authentication to the JWT-based stateless solution! If you’re interested, keep on reading 😉
      Anyways, glad I could put you on the right track. Happy coding and plenty of cheers!

Leave a Reply

Your email address will not be published. Required fields are marked *