Accounts and Progress

Learning Website: Framework & Architecture

Chapter 9 ยท Accounts, Progress & Shared Services

This is the optional chapter. Everything so far has been about publishing static pages, which needs no accounts at all. The question here is whether the sites should remember visitors: which chapters they have finished, which course they were on, perhaps a bookmark or a comment. The honest answer might be “no”, and a good design chapter says so. If the answer is “yes”, there are several ways to do it, and the multi-site layout makes some of them harder than they look.

Decide on the need first
Accounts are the most expensive feature you can add to a static site: they bring security work, stored personal data, password resets and legal duties. Add them only if a feature you really want cannot work without them. Chapters 1 to 8 and 10 to 12 do not depend on this chapter.

What Exists Today

  • The learning site has no visitor accounts. Its admin page is a read-only status dashboard, protected by Apache Basic Auth through an .htaccess file, not by a login system of its own.
  • The Anime Vault is a separate PHP and MySQL application with real accounts. It has its own login, registration, roles and admin pages. It sits inside the static site's public/ folder, so the static build carries it along.

The Anime Vault is a useful reference because it is a working, real account system. Reading its code (Exercise 3) shows bcrypt password hashing, prepared statements in 60 places, a session cookie that is HttpOnly, SameSite and Secure on HTTPS, a fresh session id at login and a CSRF token. That is a sound base. It also raises a design point for the new layout: a dynamic application with its own database does not belong inside the output folder of a static site. Give it its own site (for example anime.osztromok.com) with its own document root, and the static builds stay purely static.

Five Options

OptionWhat it givesCost
A. No accountsNothing rememberedNone. Simplest and safest.
B. Browser storage“Mark as done” and progress bars, saved in the visitor's own browserPer site only (see below), lost if the visitor clears their data or changes device
C. Export and importB plus a file the visitor can save and load on another deviceA little more code; the visitor holds their own data
D. A shared account serviceOne login, progress follows the visitor across sites and devicesThe full cost of accounts, on every site
E. Separate apps keep separate accountsEach dynamic application (like the Anime Vault) has its own loginSeveral logins, but a problem in one cannot touch the others

For a learning site, option B or C covers most of the value: “I have done chapters 1 to 4” does not need a server. They are also the options that need no personal data from visitors. Choose D only if you want progress to follow a visitor across devices or sites, or if you want comments.

Browser Storage Meets Subdomains

Chapter 1 explained that each subdomain is a separate origin. Browser storage follows origins, so localStorage on languages.osztromok.com cannot be read from systems.osztromok.com. Progress saved by the languages site is invisible on the systems site. That is fine for per-site progress bars, which is what most visitors want. It means a single “continue where you left off” list across all sites needs more than browser storage: either the shared cookie or a server.

Storage can be empty at any time
Private windows, cleared site data and blocked storage all happen, and a script that reads localStorage can throw an error. Wrap every read and write in a try block, and make the page work normally when storage is missing.

If You Do Add Accounts

One identity, or several?

A shared login is a cookie with a Domain attribute for osztromok.com, which the browser sends to every subdomain. It is convenient, and it ties all sites together: a script-injection flaw or a handed-over subdomain on any one site becomes a risk for every site. The alternative is a host-only cookie using the __Host- prefix, which browsers only accept with Secure, Path=/ and no Domain. That cookie can never be shared, so each site has its own session, normally obtained through a central login page that redirects back (single sign-on). Exercise 2 builds both headers and checks them.

Talking to an account service from another site

If a page on languages.osztromok.com calls an API on account.osztromok.com to save progress, the browser treats it as a cross-origin request (Learning Website: Framework & Architecture 1). The API has to allow that origin explicitly, and a request that carries cookies needs the API to name the exact origin rather than a wildcard and to allow credentials. Every extra allowed origin is extra attack surface, so allow only the sites that need it.

Passwords and sessions

  • Store only a salted, deliberately slow hash (bcrypt, scrypt or Argon2), never the password.
  • Compare in constant time, and regenerate the session id at login.
  • Protect state-changing requests with a CSRF token, and use POST for them, not GET.
  • Set the cookie Secure, HttpOnly and SameSite.
  • Limit login attempts, and plan password reset before launch, not after.

The course Authentication & Session Security covers each of these in depth. Do not write your own scheme from this summary: use a well-reviewed library or framework feature (Django and Next.js both have one).

What to Store, and Why Less Is Better

Progress needs very little: who, which page, when. The identity of a page is the problem. The best stable identifier is the page's root-relative path from Chapter 3, with an alias table from old paths to new ones for renames and moves, generated from the same redirect map you build in Learning Website: Framework & Architecture 11. Exercise 1 builds this with SQLite.

Personal data brings duties
Once a visitor's email address or progress is tied to an identity, you are holding personal data. If you are based in the United Kingdom, UK data protection law applies to you as the person holding it. In general that means: collect only what you need, say what you keep and why, let people see and delete their data, keep it secure, and have a plan for a breach. This is not legal advice; read the Information Commissioner's Office guidance for small organisations before you launch anything that stores personal data. Options A to C avoid most of this, which is a good reason to prefer them.

Hands-On Exercises

Exercise 1

Build a small progress store with SQLite: a completed-chapters table keyed by user and page path, an alias table for renamed paths, and functions to mark a chapter done and to report a course's progress. Test it on a real course, including marking a chapter twice and renaming one.

๐Ÿ“„ View solution
Exercise 2

Write functions that build a shared session cookie and a host-only __Host- cookie, and a checker that reports missing attributes. Then hash and verify a password with a random salt and a constant-time comparison. Say what each design choice protects against.

๐Ÿ“„ View solution
Exercise 3

Review the Anime Vault's login code with a script that checks admin pages for authentication, POST handlers for CSRF checks, SQL for interpolated variables and output for unescaped request values. Then open every flagged file and decide which findings are real.

๐Ÿ“„ View solution

Chapter 9 Quick Reference

  • Today: the learning site has no visitor accounts; the Anime Vault is a separate PHP app with its own login
  • Options: none, browser storage, export/import, a shared account service, or separate apps with separate accounts
  • Browser storage is per origin: progress saved on one subdomain is not visible on another
  • Always guard storage with try; pages must work without it
  • A shared cookie (Domain=osztromok.com) ties every subdomain's security together; __Host- cookies cannot be shared
  • Cross-origin API calls with cookies need the exact origin allowed, plus credentials
  • Passwords: salted, slow hash; constant-time compare; new session id at login; CSRF token on POST
  • A stable page id is the page's path, plus an alias table for renames and moves
  • Keep dynamic apps out of the static output: give them their own site
  • Personal data brings legal duties: prefer options that avoid collecting it (this is not legal advice)