Comment by argulane
It's mostly about performance. If you can store all the required info about the user inside the cookie then you can avoid a DB query roundtrip before sending a response.
Now that your cookie looks like this (probably also base64 encoded):
{"id": 42, "display_name": "John", "is_admin": false, "session_end_at":1726819411}
You don't have to hit the DB to display "Hi John" to the user and hide the jucy "Admin" panel. Without HMAC, an attacker could flip the "is_admin" boolean in the cookie.You could also create a cookie that is just random bytes
F2x8V0hExbWNMhYMCUqtMrdpSNQb9dwiSiUBId6T3jg
and then store it in a DB table with similar info but now you would have to query that table for each request. For small sites it doesn't matter much and if it becomes a problem you can quite easily move that info into a faster key-value store like Redis. And when Redis also becomes too slow you are forced to move to JSON Web Tokens (JWT) witch is just a more standardized base64 encoded json wrapped with HMAC to avoid querying a database for each request.But even if you are using random bytes as your session identifier, you should still wrap it in a HMAC so that you can drop invalid sessions early. Just for making it harder for someone to DDOS your DB.
Back in the Justin.tv days, we used this for some messages that were passed by the client between two of our systems: The main authentication was done by the web stack which gave the client an HMAC-signed viewing authorization. That was then passed on to the video servers which knew how to check the authorization but weren’t hooked up to the main DB.
Setting things up this way meant that we didn’t need to muck about with the video server code whenever we made policy changes as well as isolating the video system from web stack failures— If the web servers or DB went down, no new viewers could start up a stream but anyone already watching could continue uninterrupted.