Skip to content
Language:en Author:Laborci Gergely
identity

Browser storage and tokens

Objectives

By the end of the lesson, you can tell the browser storage options (cookie, Web Storage, IndexedDB) apart, you can explain what a token and a JWT are, and you understand that the concept of a token and its storage are two separate decisions.

Why two separate concepts?

A cookie, localStorage, and IndexedDB are places of storage: where we put data. A token, by contrast, is a value that a protocol hands over and checks for a defined purpose: it represents access or identification. A token can be stored in a cookie, in localStorage, or in memory, and the decision affects security too. Whoever mixes up the two asks the wrong question: the question is not "cookie or token", but what we want to prove, and where we keep it and how we forward it.

Course page, draft, and access

The student chooses a language setting, leaves a local draft unfinished, and logs in. The three data items serve different purposes:

Data Natural place Why?
Language setting (small) Cookie or localStorage Simple, not sensitive.
Unfinished draft (structured, larger) IndexedDB Structured data (3.6).
Proof of login Session cookie or token Sensitive; its handling is determined by security considerations.
flowchart TD
    A[Application need] --> C[Cookie: can be attached to the HTTP request by itself]
    A --> W[Web Storage: key–value]
    A --> I[IndexedDB: structured data]
    T[Token: proof or access value] -. separate storage decision .-> C
    T -. separate storage decision .-> W

The diagram does not recommend storing a sensitive token in Web Storage automatically; it only shows that the concept of a token and the place of storage are a separate decision.

Comparing the stores

Cookie localStorage sessionStorage IndexedDB
Scope by domain and path origin (3.6) origin + browser tab origin
Goes with the request? yes, if the scope matches no no no
Can JavaScript read it? no, if HttpOnly yes yes yes
Size about 4 kilobytes / cookie about 5 MiB / origin about 5 MiB / origin managed by the browser, large
Lifetime Max-Age / Expires until the browser deletes it until the tab closes until the browser deletes it

(The size data are based on MDN's pages: a cookie about 4 kilobytes, Web Storage about 5 MiB per origin.) Two consequences:

  • A cookie is attached to the request by itself, which is convenient (a session identifier needs this), but it also means that it can be attached to requests started from other sites, if SameSite does not prevent it (chapter 8).
  • The content of localStorage and IndexedDB can be read by any JavaScript running on the page. If an attacker can inject a script into the page (XSS, chapter 8), they can read a token stored here too. A HttpOnly cookie is not visible to JavaScript.

Local storage is not a reliable server-side record and does not synchronize to the user's other devices. The official state of an accepted enrolment must be managed by the server.

What is a token?

A token is a value that a protocol hands over and checks for a defined purpose. An access token, for example, represents access to an API resource. A server-side session identifier (7.2) is also a carrier value, but it is tied to a different mechanism. APIs often accept a token in the Authorization header, with the Bearer scheme:

GET /jelentkezesek/anna HTTP/1.1
Host: kurzuspont.example
Authorization: Bearer <token>

According to RFC 6750, the holder (the bearer) of a "bearer token" can use the token to reach the resource, without proving possession of a cryptographic key: possession itself is the authorization. That is why the token may be forwarded only over TLS (the RFC makes it mandatory), putting it into a URL is not advisable (the RFC also opposes it), because URLs end up in logs, and its lifetime should be short.

JWT: a widespread token format

Not every token is a JWT, and no JWT is automatically secure. A JWT (JSON Web Token) is, according to RFC 7519, a compact, URL-safe form of transmitting claims between two parties. The token consists of three base64url-encoded parts separated by dots: the header, the payload, and the signature. We tried it (Node.js crypto module, HS256, that is an HMAC SHA-256 signature with a shared secret):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhbm5hIiwic3plcmVwIjoiaGFsbGdhdG8iLCJleHAiOjE3OTE3NDE2MDB9._x4fXjw_K5A-qpXUeTODT1aTOQfqua55_Rd0XqhWsSg

fej      : {"alg":"HS256","typ":"JWT"}
tartalom : {"sub":"anna","szerep":"hallgato","exp":1791741600}

eredeti token                 : ÉRVÉNYES aláírás
módosított token (szerep=oktato): ÉRVÉNYTELEN aláírás

(In the output, "fej" is the header, "tartalom" the payload, "szerep" the role, "hallgato" student, "oktato" instructor; "eredeti token: ÉRVÉNYES aláírás" means "original token: VALID signature", and "módosított token: ÉRVÉNYTELEN aláírás" means "modified token: INVALID signature".)

Three lessons:

  1. The payload is not secret. The payload of a JWT can be decoded by anyone (base64url is not encryption). According to the RFC, a JWT can be signed (JWS), encrypted (JWE), or even unprotected. Secret data must not be put into a signed but not encrypted JWT.
  2. The signature protects integrity. If someone rewrites the szerep field, the signature no longer matches, so the server rejects it. The modification can therefore be noticed.
  3. The expiry is in the payload (exp, seconds from the start of Unix time), but the server must check it. If we want to revoke a token before it expires, a record is needed on the server side as well.

A JWT is typically a "self-contained" token: the server can decide its validity from the signature, without needing a session table. This is convenient, but revocation is harder. The choice of format depends on the type of client, the access boundary, and the security model.

A website can send a server-side session identifier in a cookie. An API can accept an access token in the Authorization header. In an OAuth 2.0 and OpenID Connect environment several kinds of tokens can appear, for different purposes (7.5). "Cookie or token" is therefore a false, oversimplified choice: a cookie is a mechanism of transmission and storage, whereas a token is a value within a protocol.

Worked example: the login token of Kurzuspont

Kurzuspont also has a mobile app. The browser interface uses a session cookie, and the mobile app sends an access token in the Authorization header.

Client Proof Storage Reason
Browser session cookie (HttpOnly) the browser's cookie store JavaScript cannot reach it, it is attached by itself.
Mobile app access token the secure store of the operating system There is no cookie model; a short lifetime.

In both cases the server checks the authorization (7.4), and the proof can be revoked.

Common misconceptions

Statement Clarification
"A token is the name of a browser store." It is a value; its storage is a separate design decision.
"Every token is a JWT." Several formats exist, including opaque tokens.
"The data of localStorage goes to the server automatically." Unlike a cookie, it is not attached to requests.
"A successful enrolment stored in the browser is the official result." The server-side state is authoritative.
"The data in a JWT is secret." The payload of a signed JWT is readable by anyone.

In brief

  • A cookie, Web Storage, and IndexedDB are stores; a token is a value whose storage is a separate decision.
  • A cookie is attached by itself and can be HttpOnly; the content of localStorage can be read by any script running on the page.
  • A bearer token works by possession: TLS, a short lifetime, and it is not advisable to send it in a URL.
  • A JWT is a signed format: the payload is readable, but cannot be modified unnoticed.

Review questions

  1. Why is it more dangerous to keep a token in localStorage on an XSS-vulnerable page than in a HttpOnly cookie?
  2. What does it mean that possession of a bearer token is the authorization?
  3. Why is the payload of a signed JWT readable by anyone?
  4. What does the signature protect, and what does it not?

Learning resources

Prerequisites (2)

  • JavaScript
  • application state

Teaches (2)

  • token
  • JWT