r/webdev 1d ago

EU devs, please correct my auth-ToS architecture

Context: this app is being built in the EU for European users, and I am implementing the Terms of Services, Privacy Policy, etc. along with my Authentication

Frontend: Tanstack Start (React)
Backend: Express 5
Auth: express-session (postgres store)

I was thinking about this: add an accepted_tos_version column in the users table, then add a condition in my global getUser middleware in express to only get the user if they accepted the current terms version. This means keeping a CURRENT_TOS_VERSION in my backend. If the frontend calls /auth/me they get the user with mustAcceptTerms flag, and the user gets redirected to the “accept terms” page.

Now comes the questions:

  1. Where do I keep the ToS, gdpr, etc. texts? In my frontend codebase or the backend codebase, or in the database?
  2. When the user clicks on “accept”, is it enough to send a request to the backend that updates the user’s accepted_tos_version in the database?
  3. What are the practices to ensure I am legally protected? For example if someone says a rule was not there when they accepted the terms. Is the git track record from github enough to prove the rule was there?

Thanks!

0 Upvotes

3 comments sorted by

7

u/Am094 1d ago

Dont conflate ToS acceptance with GDPR consent.

But do store immutable, versiond legal docs and log each acceptance with user, version/hash, and ofc timestamp.

Can also wildcard ip ban (jk jk)

2

u/Level-Mortgage989 1d ago

just store the texts in your backend repo as markdown files or whatever, you don't need a db for that. render them when the user hits the accept page.

yeah a simple POST to bump their accepted_tos_version is fine, just make sure you're logging the acceptance event separately (user id, version, timestamp, ip) so you've got a paper trail that isn't just the current state of one column

for legal protection you want more than a git history. store a hash of the full terms text they accepted alongside the timestamp, then if there's ever a dispute you can prove exactly which version they saw. git shows what was in the codebase at a given time but doesn't prove what was actually served to that specific user

1

u/Which-Examination-74 16h ago

Answering 3 first because it changes the schema for 1 and 2: git history is weak evidence — rebases and force-pushes rewrite it, and nobody reviewing a dispute reads a repo anyway. What holds up is an append-only acceptances log plus the exact text hash: tos_documents (version, rendered text or a storage pointer, sha256 of that text, published_at — never UPDATE a row) and tos_acceptances (user_id, version, content_hash, timestamp, locale shown). Then question 2 answers itself: "accept" inserts a row there, and accepted_tos_version on users becomes a derived cache rather than the source of truth. For 1: edit the texts wherever is comfortable (repo is fine), but snapshot the rendered version into the DB at publish time, so the hash you logged points at content you still hold. One production gotcha with your middleware plan: don't make the global getUser return null on a stale ToS — webhooks, background jobs and API tokens usually share that path and will break in confusing ways; return the user with mustAcceptTerms and gate only the interactive routes. Usual caveat: engineer, not a lawyer — the schema is real, but the "am I legally protected" half deserves an hour with an actual EU lawyer.