# 5. Sign in before joining

We now assemble the final application. Build it and create two accounts:

```sh
npm run build
npm run add-user -- alice
npm run add-user -- bob
npm start
```

Each provisioning command prints a fresh random password once. Save it. Visit **http://localhost:8181/login**, not a different hostname. Use separate browser profiles or a private window so Alice and Bob do not share one cookie jar.

## Reuse the tested sign-in implementation

The project includes the authentication and store modules from the released Redweb 0.14.0 dashboard recipe. The website generates these files from that immutable catalogue instead of maintaining a second copy of password logic. Only the relative import extension changes for this ESM project.

Passwords use asynchronous salted scrypt. Sessions are random opaque tokens, stored as hashes in SQLite, and carried in HttpOnly, SameSite=Strict cookies. Login attempts and concurrent password work are bounded. There is no default password or browser-stored bearer secret.

The existing store contains unused dashboard card tables; this tutorial reuses its account/session operations only. This is a reuse boundary, not a claim that Redweb itself provides a managed identity service.

## One identity for the page and socket

<!-- source: src/server.tsx -->

The account lookup is used for the protected TSX page and `/match` admission. Login/logout POSTs and WebSocket upgrades require the exact trusted origin. Production requires an explicit HTTPS origin; the app does not trust forwarded headers to establish identity or origin.

`Accounts.onInit()` opens the store and mounts ordinary Express routes before the listener opens. `onShutdown()` closes resources. The final `defineApp` registers the pages, game route and lifecycle service together. There is no second call to initialize a WebSocket server.

The `gameApp` factory is application code for configuring isolated instances in tests. It is **not** an extra Redweb startup primitive. The executable entry remains:

<!-- source: src/app.tsx -->

**Checkpoint:** an incorrect password is rejected through the real browser form; a correct one opens the board. An unsigned or cross-origin socket cannot join. Logout invalidates sessions before closing that account's existing sockets.
