Skip to content

2. Members

Turn authentication on: sign-up, sessions, roles, and an account page with password change and account deletion.

Chapter 1’s realm was open to anyone on localhost. Now members sign up and sign in — and the app learns who you are: your gallery, your assets, your account page.

The realm grows

Replace config/shelf/realm.yaml:

auth: enabled
signup: open

roles:
  admin:
    description: "Operators: users, invitations and API keys"
    admin: true
    users:
      - operator@shelf.example
  curator:
    description: Publishes assets and manages share links (chapter 5)
    users:
      - curator@shelf.example
  • auth: enabled — users, sessions and roles exist; anonymous callers get 401.
  • signup: open — anyone may create an account (alternatives: invite, closed). It is open only for this chapter and the next: chapter 4 closes it, and from then on every new member needs an invitation — which only an existing administrator can create.
  • roles: — admin is the operator role (admin: true grants the admin API); curator matters from chapter 5. The users: lists are seed assignments: when someone signs up with a listed address, the role attaches. A shortcut for local stacks — never keep seeded users in a real realm.

Apply, and create the three accounts

docker compose restart backd

Open http://localhost:8080 and sign up (any password of 12 or more characters; the demo stack sends no email yet and account.require_verified_email is off, so nothing needs verifying — the verify-email and welcome messages only appear once chapter 10 wires email) as each of:

EmailWhy you need it
operator@shelf.examplethe administrator: chapter 4 invites with it, chapters 10 and 12 use the Admin view
curator@shelf.examplethe curator: publishes assets from chapter 5 on
your own addressa plain member, to see what the rules refuse

Do it now. Once chapter 4 sets signup: invite, a new address can’t sign up without an invitation, and nobody can create an invitation without an administrator — so an operator who hasn’t signed up yet would lock you out (the way back is backd bootstrap, but it is easier not to need it). Log out between accounts (Log out in the nav), or use a private window for each.

backd bootstrap --realm shelf --email you@example.com is how a real deployment creates its first admin, and the way out of that lock-out.

Signing in

The app’s Sign in button now does something: backd.auth.signup() or .login() returns a session the client keeps in localStorage (storage: localStorageStorage('shelf') — it survives reloads). backd.auth.me() on load restores it. A token in localStorage is readable by any script on the page, so a real app guards against XSS — or uses HttpOnly session cookies, which the client supports with cookies: true.

What the app gains

  • Nav state: backd.auth.me() at load; sign-in button ↔ email + log out.
  • My assets: assets.list({ where: { "_meta.owner": user.id } }) — _meta fields are queryable like any other.
  • Account page: backd.auth.changePassword() works now; email change and password reset are visible but disabled — they need the email stack of chapter 10.
  • Delete my account: backd.auth.deleteAccount() deactivates — your email stays registered, your documents stay. Actual erasure is an admin action that applies each collection’s policy (Deleting and erasing users):

Create config/shelf/main/assets/collection.yaml, and the same file for shares, then restart:

# What happens when an administrator erases a member: their assets go with
# them (and so do their share links, from the same file in shares/).
on_owner_delete:
  action: delete

action: delete says a member’s assets — and their share links — go with them. The blog example shows the alternative (anonymize). collection.yaml is where a collection’s policy lives: this chapter puts its erase policy in it, chapter 3 adds the access rules, and chapter 13 the file fields.

You should see

  • GET /v1/shelf/main/assets without a session answers 401 — the chapter-1 curl now needs a sign-in first.
  • Signing up and reloading keeps you signed in; Log out ends it.
  • operator@shelf.example and curator@shelf.example exist, with the password you chose, next to your own account — and the operator’s session has roles: ["admin"] (backd.auth.me()).
  • My assets lists only what you created; the account page changes your password and the new one signs in.
  • Deleting your account signs you out; signing up again with the same address says it’s taken (deactivated, not gone).

Next: chapter 3 — the rules: of each collection decide who may touch what.

Edit this page · Last updated 08 Oct 2026