Session Cookies vs. A Devise-JWT for Authenticating Users in Ruby on Rails
This article will explain the differences between Devise-JWT and Sessions, as well as which one to use for authentication.
Recently, I had to implement authentication on one of the Rails projects I was assigned to work on. through my research on the best approach to use to create a user authentication API. I found several articles, especially related to the Devise-JWT-Token since they used Ruby on Rails for the API creation.
After configuring the authentication with Devise JWT, I began to wonder if there was a better way to implement user authentication than the Devise-JWT token I had previously used. That pushed my ideas far, and I ended up finding that there are two primary different approaches in Ruby on Rails that you can use for session management.
- Session or Cookies, and
- JWT (JSON Web Tokens)
As you might remember, HTTP is a stateless protocol. This means that your server doesn’t remember anything from previous requests. We generally need to maintain login “sessions”, so that once a user logs in for the first time, they should be able to continue to access different parts of the application without having to log in again and again for each subsequent “http” request made by the server.
Why are session cookies and Devise-JWT important?
Consider how websites remember how a user is logged in when the page reloads. We need to allow websites to remember users within a website when they move between different parts of the application. So, how does it work when a user signs in and your server remembers?
So, the only way to remember the states of your application is by using either sessions or tokens.
In web applications, after successful authentication, the server generates a “cookie” in the case of session-cookie, or an “accesstoken” in the case of device-JWT. Those are the two mechanisms that Rails and your browser use to store and retrieve the state for a user in order to maintain login sessions in the application.
I. Session Cookies
When you browse the web, you use HTTP requests, which is a stateless protocol. So, how can you tell that a given request actually came from that particular user who is logged in? This is why cookies are important. They allow you to keep track of your users from one request to another until the cookie expires.
=> What is a session?
A session is just a place to store data from one request to then read it on later requests. For example, after a user is logged in, you can store the user’s ID in the session so on later requests you can fetch the user and know the request is authenticated and get the user’s details. Also, a session can have different mechanisms to store the data. The ones used in most cases are “cookies”. You can give an identifier value to the user’s browser, and then on every request you get it back and can use it to fetch the session and corresponding data.
If you’d like to learn more, visit this link on how rails sessions work.
=> What is a cookie?
Cookies are key-value pairs that are stored in the browser and sent on every request to their domain. They can be used for anything, but in this case, they are used to store a “credential” that can identify the session for the user. If you’d like to learn more, visit this Rails guide: cookies.
Steps in Session Cookie
=> After successful authentication1. The Server generates a "sessionId" (signs it using "secret key"), and saves the sessionId in a sessionDB, and sends a cookie with the sessionId to the browser (client side).2. The browser (client side) receives the "cookie" in the response from the server, and saves it in the cookie storage.3. The browser then includes the "cookie" in every subsequent request to the server.=> User is authenticated and the client side has a "cookie" 4. Get the "sessionId" from the request "cookie".5. Verify the "sessionId" integrity using the "secret key".
Then look up the "sessionId" within the sessionDB on the server and get the "userId".6. Look up the "userId" within the shoppingCartDB to add items to cart for that userId, or display cart info for that userId..
Note:
- The sessionId does not contain any “userId” information, but is a random string generated and signed by the “secret key.” The sessionId is then saved within the sessionDB. The sessionDB is a database table that maps “sessionId” to “userId”. The session cookie is called the “stateful” approach to managing sessions, since the “state” or “session” is saved within a DB. A sessionDB stores sessionIds.
II. Devise-JWT ( accessToken )
Devise-jwt is a device extension that uses JSON Web Tokens(JWT) for user authentication. It’s an encoded, URL-safe string that can contain an unlimited amount of data (unlike a cookie) and is cryptographically signed. When a server receives a JWT, it can guarantee the data it contains can be trusted because it’s signed by the source.
userId is got by decrypting the JWT token (no DB call is required to get userId), so this is somewhat faster than the session approach. Servers can be scaled separately, without the need to share session DB. This makes the JWT approach a great option for micro-services architecture.
On the client-side, the browser stores the token locally using local storage, session storage, or cookie storage. On future requests, the JWT is added to the authorization header prefixed by the bearer, and the server will validate its signature by decoding the token before proceeding to send a response.
Steps in Devise -JWT
=> After successful authentication1. The server generates an "accessToken", encrypting the "userId" and "expiresIn", with the ACCESS_TOKEN_SECRET,
and sends the "accessToken" to the browser (client side).2. The browser (client side) receives the "accessToken" and saves it on the client side.3. The "accessToken" is included in every subsequent request to the server.=> User is authenticated and the client side has an "accessToken".4. Get the "accessToken" from the request "header".5. Decrypt the "accessToken" i.e the JWT, using ACCESS_TOKEN_SECRET and get the "userId" ---> (there is no DB lookup).6. Look up the "userId" within the shoppingCartDB to add items to cart for that userId, or display cart info for that userId.
Note:
- Devise-JWT uses a “secret key” specified in our credentials file to build the JWT tokens, sometimes referred to as “Bearer Tokens” since all the information about the user i.e. “bearer”, is contained within the token. The token is added to the request headers themselves (rather than stored/retrieved as a cookie). This isn’t performed automatically by the browser (as with cookies) but is typically handled by a front-end framework as part of an AJAX call. All the information about the user is contained within the accessToken ( the encrypted “userId”), and the accessToken is not saved within any sessionDB on the server side like cookies, it only exists on the client side. That is why it is called a stateless approach to managing sessions since no “state” or “session” is saved within a DB (it is contained within the JWT token itself).
Additional Note:
- We can use either the “Session-Cookie” or the “Devise-JWT” (widely used in modern web applications) to implement the session management. The main difference is that in the case of the device-JWT, the server does not need to maintain a DB of sessionId for lookup. Because the tokens are stored on the client side while the session uses server memory to store user data, this may cause a problem if a large number of users access the application at the same time.
- We can also combine the JWT with cookies. On every request to the server, the JWT will be read from cookies and added to the authorization header using the bearer scheme. The server can then verify the JWT in the request header (as opposed to reading it from the cookies). If you need to learn more, visit this link on how to send JWT tokens to Cookie.
Conclusion
- In the article, you’ve learned that we can use “cookies” in the case of a session approach and an “accessToken” in the case of a device-JWT approach for authentication. In both approaches, the client side must securely save the “cookie” or the “jwt token”.
Written by Ben Mukebo.
I am a software developer, familiar with a variety of different web technologies and frameworks, and keen on always finding ways to explain things as simply as possible.
If this article has been helpful, please hit the 👏 button and share it with others! 🚀 to show how much you liked it! 😄