In the early Web, when sites only consisted of static content, the HTTP protocol already offered an Authorization header, which could be used for authentication and authorization, even up until this day. The most common application of the Authorization header uses Basic authentication, where the user’s credentials, more specifically a username and password, are base 64- encoded, and included as the header value. This allows a server-side application to extract these credentials, verify them against a password file and make a decision on whether to allow the request or not. After successful authentication, the browser will attach the user’s credentials to every subsequent request to this origin. Note that since the browser remembers these credentials during the lifetime of its process, logging out of an account is only possible by closing the browser.
As Web applications became more complex, developers wanted to integrate authentication with the application, streamlining the user experience within the same look and feel. To authenticate users, they embedded an HTML form, where the user had to enter a username and a password. By submitting the form, the username and password were sent to the server, where they could be validated. However, since the credentials are only sent in a single request after form submission, instead of in every subsequent request as with the Authorization header, Web applications needed a way to keep track of the user’s authentication state across requests, a challenge with the stateless HTTP protocol.
A session management mechanism on top of HTTP is capable of associating multiple requests from the same user with a server-side session state, allowing the application to store user-session information, such as an authentication state or a shopping cart. To keep track of this server-side session across multiple requests, the session is assigned an identifier, which the client needs to include in every request. Initially, this identifier was embedded in the URI as a parameter, for example, http://example.com/index.html?SID=1234. Unfortunately, this requires the Web application to append the user’s session identifier to every URL on the page, and repeat this action for every user. The introduction of cookies addresses this problem. Cookies are server-provided key-value pairs, stored by the client in the cookie jar and attached to every request to the same domain.
Essentially, cookies allow the server to store small pieces of data at the client-side, which will be attached to future requests. Cookies can be used for storing simple settings, such as a language preference, or for building more complex systems, such as session management mechanisms. Today, cookie-based session management is the de facto standard, but many systems still allow parameter-based session management as a fallback mechanism, for example when cookies are not supported by the browser, or disabled by the user.
In this de facto standard session management mechanism, the session identifier has a peculiar property, since it suffices to present the associated session identifier to the Web application in order to associate a request with a specific session. This essentially makes the session identifier a bearer token, meaning that a request bearing the identifier is granted the privileges associated with the session. Since the server-side session state generally stores the user’s authentication state, these privileges are comparable to those of an authenticated user. Note that this means that the session identifier and credentials are similar, as they both grant access to an authenticated session. Credentials, however, can be used to repeatedly gain access to the application, while the level of access granted by the session identifier is limited to the lifespan of the session.