Major auth refactor replacing Bearer token authentication with HttpOnly cookie-based auth, removing the registration endpoint, and adding the PASS IRC command for password management.
Changes
Removed
POST /api/v1/register endpoint (no separate registration path)
RegisterUser DB method
Authorization: Bearer header parsing
token field from all JSON response bodies
Token field from CLI SessionResponse type
Added
Cookie-based authentication: neoirc_auth HttpOnly cookie set on session creation and login
PASS IRC command: set a password on the authenticated session via POST /api/v1/messages {"command":"PASS","body":["password"]} (minimum 8 characters)
- Remove POST /api/v1/register endpoint entirely
- Session creation (POST /api/v1/session) now sets neoirc_auth HttpOnly
cookie instead of returning token in JSON body
- Login (POST /api/v1/login) now sets neoirc_auth HttpOnly cookie
instead of returning token in JSON body
- Add PASS IRC command for setting session password (enables multi-client
login via POST /api/v1/login)
- All per-request auth reads from neoirc_auth cookie instead of
Authorization: Bearer header
- Cookie properties: HttpOnly, SameSite=Strict, Secure when behind TLS
- Logout and QUIT clear the auth cookie
- Update CORS to AllowCredentials:true with origin reflection
- Remove Authorization from CORS AllowedHeaders
- Update CLI client to use cookie jar (net/http/cookiejar)
- Remove Token field from SessionResponse
- Add SetPassword to DB layer, remove RegisterUser
- Comprehensive test updates for cookie-based auth
- Add tests: TestPassCommand, TestPassCommandShortPassword,
TestPassCommandEmpty, TestSessionCookie
- Update README extensively: auth model, API reference, curl examples,
security model, design principles, roadmap
closes#83
Cookie properties (HttpOnly, Secure when TLS, SameSite=Strict)
✅
setAuthCookie() correctly sets all three. Secure checks both request.TLS and X-Forwarded-Proto for reverse proxy support. clearAuthCookie() mirrors the same properties with MaxAge: -1.
2
No token in JSON responses
✅
handleCreateSession and handleLogin both return only {"id", "nick"}. SessionResponse type in CLI also has only ID + Nick. Test TestSessionCookie explicitly asserts no token key exists in the JSON body.
3
PASS command (min 8 chars, bcrypt)
✅
handlePass validates empty/missing body → ERR_NEEDMOREPARAMS (461), short password (<8) → ERR_NEEDMOREPARAMS, then calls database.SetPassword() which uses bcrypt.GenerateFromPassword at default cost. Three tests cover this: TestPassCommand, TestPassCommandShortPassword, TestPassCommandEmpty.
4
Register endpoint removed
✅
/register route gone from routes.go. HandleRegister function removed. RegisterUser DB method removed. grep confirms no register references remain (only IRC numerics).
5
Bearer auth removed
✅
grep -rn "Bearer|Authorization" --include="*.go" returns nothing. authSession() now reads from request.Cookie(authCookieName). Authorization removed from CORS AllowedHeaders.
6
CORS with AllowCredentials: true
✅
Uses AllowOriginFunc (reflects origin, not *), AllowCredentials: true.
7
Logout/QUIT clear cookie
✅
Both handleQuit and HandleLogout call clearAuthCookie() which sets MaxAge: -1.
8
CLI client uses cookie jar
✅
Client.Token field removed. NewClient() creates cookiejar.New(nil) and sets Jar on HTTPClient. PollMessages shares the jar. do() no longer sets Authorization header.
- Fix false claim 'clients never need to handle the token directly' —
CLI clients (curl, custom HTTP clients) must explicitly manage cookies
- Replace 'token' with 'cookie' in multi-client diagram (token_a → cookie_a)
- Fix Set-Cookie placeholders in protocol diagrams (<token> → <random_hex>/<cookie_a>/<cookie_b>)
- Fix 'old token' → 'old auth cookie' in QUIT command description
- Fix 'get token' → 'get auth cookie' in Client Development Guide
- Fix 'Tokens are hashed' → 'Cookie values are hashed' in Security Model
- Fix 'client tokens are deleted' → 'client auth cookies are invalidated'
- Fix 'Cookie sent automatically' → 'Cookie must be sent' in diagram
- Fix 'eliminates token management from client code entirely' rationale
- Fix 'No token appears in the JSON body' → 'No auth credential appears'
- Fix 'encoded in the token' → 'encoded in the cookie value'
- Fix 'Clients never handle tokens directly' in JWT comparison section
- Update clients table token column description for clarity
- All remaining 'token' refs verified as legitimate (pow_token/hashcash/JWT comparison/DB schema column name)
Rework: Fixed ALL documentation inaccuracies about cookie-based auth
Addressing sneak's review feedback on PR #84 (closes #83).
Fixes Applied
CRITICAL — "clients never need to handle the token directly" (line ~165): Replaced this false claim with accurate text: "Browsers handle cookies automatically. CLI clients (curl, custom HTTP clients) must explicitly save and send cookies — e.g., using curl's -c/-b flags or an HTTP cookie jar in their language's HTTP library."
"Clients never handle tokens directly" (line ~367, JWT comparison section): Same false claim repeated. Fixed to: "Browsers and HTTP cookie jars manage cookies automatically; CLI clients must explicitly save and resend cookies (e.g., curl -c/-b flags)"
Rationale section (line ~199): Fixed "eliminates token management from client code entirely" → "simplifies credential management — browsers handle cookies automatically, and CLI clients just need a cookie jar"
Client Development Guide (line ~2231): "→ get token" → "→ get auth cookie"
Security Model (line ~1836): "Tokens are hashed (SHA-256) before storage" → "Cookie values are hashed (SHA-256) before storage"
Session cleanup (line ~2087): "Individual client tokens are deleted" → "Individual client auth cookies are invalidated"
Session Creation (line ~162-163): "containing a cryptographically random token" → "containing a cryptographically random value"; "No token appears in the JSON body" → "No auth credential appears in the JSON body"
Common Properties (line ~191): "encoded in the token" → "encoded in the cookie value"
Protocol diagram (line ~406): "Cookie sent automatically on all subsequent requests" → "Cookie must be sent on all subsequent requests"
API Reference POST /api/v1/session (line ~1154-1155): "containing the auth token. The JSON body does not include the token" → "containing an opaque auth value. The JSON body does not include the auth credential"
Database schema (line ~2019): Updated token column description from "Unique auth token" → "Auth cookie value" (column name kept as-is since it matches the actual code)
Verified Non-Issues ("token" references that are correct)
Lines 359-360: JWT comparison section — "token format" refers to JWTs, correct in context
Line 2413: "proof-of-work token" — hashcash terminology, correct
Line 2504: "token buckets" — rate limiting algorithm term, unrelated
Line 2565: "replace Bearer tokens" — historically accurate description of what was replaced
Lines 2019, 2023: Database column named token — matches actual code, kept as-is
Build Status
make fmt✅ passes
docker build .❌ pre-existing test timeout in internal/handlers (30s timeout) — confirmed identical failure on the branch before this commit (README-only changes cannot affect tests)
## Rework: Fixed ALL documentation inaccuracies about cookie-based auth
Addressing sneak's review feedback on [PR #84](https://git.eeqj.de/sneak/chat/pulls/84) (closes [#83](https://git.eeqj.de/sneak/chat/issues/83)).
### Fixes Applied
1. **CRITICAL — "clients never need to handle the token directly"** (line ~165): Replaced this false claim with accurate text: *"Browsers handle cookies automatically. **CLI clients (curl, custom HTTP clients) must explicitly save and send cookies** — e.g., using curl's `-c`/`-b` flags or an HTTP cookie jar in their language's HTTP library."*
2. **"Clients never handle tokens directly"** (line ~367, JWT comparison section): Same false claim repeated. Fixed to: *"Browsers and HTTP cookie jars manage cookies automatically; CLI clients must explicitly save and resend cookies (e.g., curl `-c`/`-b` flags)"*
3. **Rationale section** (line ~199): Fixed *"eliminates token management from client code entirely"* → *"simplifies credential management — browsers handle cookies automatically, and CLI clients just need a cookie jar"*
4. **Multi-client diagram** (lines ~237-239): `token_a, token_b, token_c` → `cookie_a, cookie_b, cookie_c`
5. **Set-Cookie placeholders in diagrams** (lines ~398, 431, 447): `<token>` → `<random_hex>`, `<token_a>` → `<cookie_a>`, `<token_b>` → `<cookie_b>`
6. **QUIT command description** (line ~834): *"old token"* → *"old auth cookie"*
7. **Client Development Guide** (line ~2231): *"→ get token"* → *"→ get auth cookie"*
8. **Security Model** (line ~1836): *"Tokens are hashed (SHA-256) before storage"* → *"Cookie values are hashed (SHA-256) before storage"*
9. **Session cleanup** (line ~2087): *"Individual client tokens are deleted"* → *"Individual client auth cookies are invalidated"*
10. **Session Creation** (line ~162-163): *"containing a cryptographically random token"* → *"containing a cryptographically random value"*; *"No token appears in the JSON body"* → *"No auth credential appears in the JSON body"*
11. **Common Properties** (line ~191): *"encoded in the token"* → *"encoded in the cookie value"*
12. **Protocol diagram** (line ~406): *"Cookie sent automatically on all subsequent requests"* → *"Cookie must be sent on all subsequent requests"*
13. **API Reference POST /api/v1/session** (line ~1154-1155): *"containing the auth token. The JSON body does not include the token"* → *"containing an opaque auth value. The JSON body does not include the auth credential"*
14. **Database schema** (line ~2019): Updated `token` column description from *"Unique auth token"* → *"Auth cookie value"* (column name kept as-is since it matches the actual code)
### Verified Non-Issues ("token" references that are correct)
- Lines 359-360: JWT comparison section — "token format" refers to JWTs, correct in context
- Lines 1089, 1142, 1147, 1153, 1191, 1200, 2421, 2478: `pow_token` / hashcash proof-of-work — not auth tokens
- Line 2413: "proof-of-work token" — hashcash terminology, correct
- Line 2504: "token buckets" — rate limiting algorithm term, unrelated
- Line 2565: "replace Bearer tokens" — historically accurate description of what was replaced
- Lines 2019, 2023: Database column named `token` — matches actual code, kept as-is
### Build Status
- `make fmt` ✅ passes
- `docker build .` ❌ pre-existing test timeout in `internal/handlers` (30s timeout) — confirmed identical failure on the branch **before** this commit (README-only changes cannot affect tests)
"it says 'clients never need to handle the cookie' - this is false"
✅ FIXED
Phrase eliminated. README now states: "CLI clients (curl, custom HTTP clients) must explicitly save and send cookies" (bold, line ~166). Same correction in JWT comparison section (line ~371) and rationale (line ~200).
2
"those documentation inaccuracies aren't nits - they are failures"
✅ ADDRESSED
All 14 inaccuracies identified in the rework commit have been corrected. None remain.
3
"pipeline manager, you should have caught this"
✅ NOTED
Process issue — not a code fix. The previous reviewer incorrectly labeled blocking doc failures as "non-blocking nits." This review treats all doc inaccuracies as blocking.
2. Documentation Accuracy Verification
Exhaustive grep of README.md for stale references:
grep -i 'never need to handle\|never handle\|clients never' → zero matches✅
grep -i 'Bearer' → 1 match: line 2565 in completed TODO item [x] Cookie-based auth — HttpOnly cookies replace Bearer tokens — historically accurate, correct ✅
grep -i '/register' → zero matches✅
grep -i 'Authorization' → zero matches in README or Go source✅
All token references in README verified non-stale:
pow_token (hashcash proof-of-work) — 7 occurrences, all correct
JWT comparison section — discusses why NOT to use JWTs, correct context
Database schema token column — documented as "Auth cookie value (SHA-256 hash of the 64-hex-char cookie)"
token buckets (rate limiting term) — unrelated to auth
Completed TODO item referencing historical Bearer tokens — correct
Previous review's "nits" verified fixed:
~"old token" (line 834) → now "old auth cookie" ✅
~"→ get token" (line 2231) → now "→ get auth cookie" ✅
~token_a, token_b, token_c (lines 237-239) → now cookie_a, cookie_b, cookie_c✅
3. Requirements Checklist
#
Requirement
Status
1
Remove POST /api/v1/register
✅ Route removed from routes.go, handler removed from auth.go, RegisterUser DB method removed
2
HttpOnly cookies (HttpOnly, Secure, SameSite=Strict)
✅setAuthCookie() sets all three; Secure checks request.TLS and X-Forwarded-Proto
3
POST /api/v1/session sets cookie
✅handleCreateSession calls setAuthCookie, JSON body has no token
⚠️Rebase required before merge. The PR branch is based on bf4d63b but main has advanced to db3d23c (PR #82). Merge conflicts exist in 6 files: README.md, internal/db/auth.go, internal/db/auth_test.go, internal/handlers/api.go, internal/handlers/auth.go, pkg/irc/commands.go. The code is correct; it just needs to be rebased onto current main.
## Review: PR #84 — Auth Refactor (post-rework — doc fixes)
**Verdict: PASS** ✅
---
### 1. sneak's Comments — Verification
| # | sneak's Comment | Status | Verification |
|---|----------------|--------|-------------|
| 1 | _"it says 'clients never need to handle the cookie' - this is false"_ | ✅ FIXED | Phrase eliminated. README now states: **"CLI clients (curl, custom HTTP clients) must explicitly save and send cookies"** (bold, line ~166). Same correction in JWT comparison section (line ~371) and rationale (line ~200). |
| 2 | _"those documentation inaccuracies aren't nits - they are failures"_ | ✅ ADDRESSED | All 14 inaccuracies identified in the rework commit have been corrected. None remain. |
| 3 | _"pipeline manager, you should have caught this"_ | ✅ NOTED | Process issue — not a code fix. The previous reviewer incorrectly labeled blocking doc failures as "non-blocking nits." This review treats all doc inaccuracies as blocking. |
### 2. Documentation Accuracy Verification
Exhaustive grep of README.md for stale references:
**`grep -i 'never need to handle\|never handle\|clients never'`** → **zero matches** ✅
**`grep -i 'Bearer'`** → 1 match: line 2565 in completed TODO item `[x] Cookie-based auth — HttpOnly cookies replace Bearer tokens` — historically accurate, correct ✅
**`grep -i '/register'`** → **zero matches** ✅
**`grep -i 'Authorization'`** → **zero matches in README or Go source** ✅
**All `token` references in README verified non-stale:**
- `pow_token` (hashcash proof-of-work) — 7 occurrences, all correct
- JWT comparison section — discusses why NOT to use JWTs, correct context
- Database schema `token` column — documented as _"Auth cookie value (SHA-256 hash of the 64-hex-char cookie)"_
- `token buckets` (rate limiting term) — unrelated to auth
- Completed TODO item referencing historical Bearer tokens — correct
**Previous review's "nits" verified fixed:**
- ~~"old token" (line ~834)~~ → now "old auth cookie" ✅
- ~~"→ get token" (line ~2231)~~ → now "→ get auth cookie" ✅
- ~~`token_a, token_b, token_c` (lines ~237-239)~~ → now `cookie_a, cookie_b, cookie_c` ✅
### 3. Requirements Checklist
| # | Requirement | Status |
|---|------------|--------|
| 1 | Remove `POST /api/v1/register` | ✅ Route removed from `routes.go`, handler removed from `auth.go`, `RegisterUser` DB method removed |
| 2 | HttpOnly cookies (HttpOnly, Secure, SameSite=Strict) | ✅ `setAuthCookie()` sets all three; `Secure` checks `request.TLS` and `X-Forwarded-Proto` |
| 3 | `POST /api/v1/session` sets cookie | ✅ `handleCreateSession` calls `setAuthCookie`, JSON body has no token |
| 4 | PASS command (min 8 chars, bcrypt) | ✅ `handlePass` validates length, `SetPassword` uses `bcrypt.GenerateFromPassword` |
| 5 | `POST /api/v1/login` sets cookie | ✅ `handleLogin` calls `setAuthCookie`, JSON body has no token |
| 6 | CORS: `AllowCredentials: true` + origin reflection | ✅ `AllowOriginFunc` returns true, `AllowCredentials: true`, `Authorization` removed from headers |
| 7 | Logout + QUIT clear cookie | ✅ Both call `clearAuthCookie()` with `MaxAge: -1` |
| 8 | CLI client updated (cookie jar) | ✅ `cookiejar.New(nil)` in `NewClient()`, `Token` field removed, no `Authorization` header |
| 9 | README extensively updated | ✅ Auth model, API reference, curl examples (`-c`/`-b`), Python/JS examples, security model, roadmap |
### 4. Policy Compliance
- No `.golangci.yml` modifications ✅
- No test weakening — tests are actually stronger (new `TestSessionCookie` validates HttpOnly, SameSite, no-token-in-body) ✅
- No linter config changes ✅
- `make fmt-check`, `make lint`, `make test` all pass ✅
- Proper error handling with IRC numeric replies for PASS command failures ✅
### 5. Build Result
```
docker build --no-cache . — PASSED ✅
make fmt-check: PASSED
make lint: PASSED
make test: ALL TESTS PASSED
internal/handlers: 20.3s (59.1% coverage)
internal/db: 2.6s (61.1% coverage)
internal/hashcash: 1.0s (88.1% coverage)
internal/stats: 1.0s (100.0% coverage)
pkg/irc: 1.0s (100.0% coverage)
Binary builds: neoircd + neoirc-cli PASSED
```
### 6. Merge Note
⚠️ **Rebase required before merge.** The PR branch is based on `bf4d63b` but `main` has advanced to `db3d23c` (PR #82). Merge conflicts exist in 6 files: `README.md`, `internal/db/auth.go`, `internal/db/auth_test.go`, `internal/handlers/api.go`, `internal/handlers/auth.go`, `pkg/irc/commands.go`. The code is correct; it just needs to be rebased onto current `main`.
Rebased feature/cookie-auth-refactor onto current main (db3d23c) which includes PR #82 (username/hostname/OPER support).
Conflicts Resolved
pkg/irc/commands.go — kept both CmdOper (PR #82) and CmdPass (PR #84)
internal/db/auth.go — removed RegisterUser (PR #84's intent), kept SetPassword with correct signature; preserved SetBcryptCost and LoginUser with IP/hostname params from PR #82
internal/db/auth_test.go — updated all tests to use CreateSession(ctx, nick, "", "", "") (PR #82's 5-arg signature) + SetPassword pattern instead of removed RegisterUser; removed PR #82's RegisterUser-specific tests
internal/handlers/auth.go — removed HandleRegister (PR #84's intent), kept HandleLogin with cookie auth and IP/hostname resolution
internal/handlers/api.go — kept clientIP and resolveHostname utilities from PR #82; kept cookie-based auth from PR #84; fixed payload.Nick → nick in executeCreateSession (variable not in scope after conflict resolution)
internal/handlers/api_test.go — updated createSessionWithUsername helper to extract auth cookie instead of JSON token (matching PR #84's cookie-based auth)
README.md — removed /register endpoint docs (PR #84); merged clients table schema to include both cookie description (PR #84) and ip/hostname columns (PR #82)
Verification
make fmt✅
docker build .✅ (lint, fmt-check, all tests pass)
## Rebase Complete
Rebased `feature/cookie-auth-refactor` onto current `main` (`db3d23c`) which includes [PR #82](https://git.eeqj.de/sneak/chat/pulls/82) (username/hostname/OPER support).
### Conflicts Resolved
1. **`pkg/irc/commands.go`** — kept both `CmdOper` (PR #82) and `CmdPass` (PR #84)
2. **`internal/db/auth.go`** — removed `RegisterUser` (PR #84's intent), kept `SetPassword` with correct signature; preserved `SetBcryptCost` and `LoginUser` with IP/hostname params from PR #82
3. **`internal/db/auth_test.go`** — updated all tests to use `CreateSession(ctx, nick, "", "", "")` (PR #82's 5-arg signature) + `SetPassword` pattern instead of removed `RegisterUser`; removed PR #82's `RegisterUser`-specific tests
4. **`internal/handlers/auth.go`** — removed `HandleRegister` (PR #84's intent), kept `HandleLogin` with cookie auth and IP/hostname resolution
5. **`internal/handlers/api.go`** — kept `clientIP` and `resolveHostname` utilities from PR #82; kept cookie-based auth from PR #84; fixed `payload.Nick` → `nick` in `executeCreateSession` (variable not in scope after conflict resolution)
6. **`internal/handlers/api_test.go`** — updated `createSessionWithUsername` helper to extract auth cookie instead of JSON token (matching PR #84's cookie-based auth)
7. **`README.md`** — removed `/register` endpoint docs (PR #84); merged clients table schema to include both cookie description (PR #84) and `ip`/`hostname` columns (PR #82)
### Verification
- `make fmt` ✅
- `docker build .` ✅ (lint, fmt-check, all tests pass)
Remove /register endpoint and RegisterUser DB method
✅ Route removed from routes.go, HandleRegister replaced with HandleLogin, RegisterUser replaced with SetPassword
2
HttpOnly cookies (HttpOnly, Secure, SameSite=Strict)
✅setAuthCookie sets all three; Secure conditional on TLS/X-Forwarded-Proto
3
POST /api/v1/session creates session + sets cookie
✅ Token removed from JSON body, cookie set via setAuthCookie
4
PASS IRC command for password setting (min 8 chars, bcrypt)
✅handlePass in auth.go, SetPassword in db/auth.go, CmdPass constant added
5
POST /api/v1/login sets cookie for additional clients
✅ Cookie set, token removed from JSON response, channel state initialization added
6
CORS: AllowCredentials: true
✅AllowOriginFunc reflects origin, AllowCredentials: true, Authorization removed from AllowedHeaders
7
Logout and QUIT clear cookie
✅clearAuthCookie called in both HandleLogout and handleQuit
8
CLI updated with cookie jar
✅cookiejar.New() in NewClient, Token field removed, Authorization header removed from do() and PollMessages
9
README updated
✅ Extensive rewrite — see doc accuracy section below
CRITICAL: Documentation Accuracy Check
"Clients never need to handle the cookie" false claim:✅ NOT PRESENT. Grep for never need to handle, never handle, clients never — zero matches. The rework fix survived the rebase.
Remaining Bearer / Authorization references in README:✅ None found (only the TODO line mentioning "cookies replace Bearer tokens" which is correct).
Remaining Bearer / Authorization references in Go code:✅ None found.
Remaining /register or RegisterUser references in Go code:✅ None found.
Cookie handling accuracy in README:✅ Multiple passages correctly state CLI clients MUST manage cookies (e.g., curl -c/-b flags, Python requests.Session, etc.).
❌ BLOCKING: Stale register/registration references in README
Three README passages still reference the removed registration flow:
Line 215: "username field in the session/register request" — The /register endpoint no longer exists. Should say "session request" or "session creation request".
Line 221: "created at session creation, registration, or login" — Registration is no longer a thing. Should say "session creation or login".
Line 2471: "on session creation, registration, or NICK change" — HTTP 409 cannot occur on registration anymore. Should say "on session creation or NICK change".
These are documentation inaccuracies — they describe a flow that no longer exists in the codebase.
Code Quality Notes (non-blocking)
Clean separation of concerns: setAuthCookie/clearAuthCookie helpers are well-structured
CORS AllowOriginFunc correctly reflects origin (required for AllowCredentials: true)
Test coverage is solid — new TestSessionCookie verifies HttpOnly + SameSite properties, TestPassCommand* tests cover happy path, short password, and empty body
TestLoginValid correctly extracts cookie from response instead of JSON body
handlePass uses respondIRCError with ErrNeedMoreParams (461) for IRC-level errors, consistent with other command handlers
REPO_POLICIES Compliance
✅ All external references pinned by hash (Dockerfile base images)
✅ Makefile targets present and functional
✅make check passes without modifying files
✅ No secrets committed
✅ No .golangci.yml modifications
✅ No git add -A or git add .
Verdict: ❌ FAIL
Reason: 3 stale documentation references to the removed /register flow survive in README.md (lines 215, 221, 2471). Documentation inaccuracies are blocking per review policy.
To pass: Replace "session/register request" → "session request", "session creation, registration, or login" → "session creation or login", "session creation, registration, or NICK change" → "session creation or NICK change". Everything else is clean.
## Review: PR #84 — Cookie-Based Auth Refactor (post-rebase)
**Reviewer:** clawbot (automated)
**Branch:** `feature/cookie-auth-refactor` (2 commits on `main` @ db3d23c)
**Closes:** [#83](https://git.eeqj.de/sneak/chat/issues/83)
---
### Build Result
✅ `docker build .` — **PASS**
- `make fmt-check` — pass
- `make lint` — 0 issues
- `make test` — all packages pass (handlers 66.6% coverage, db 51.6%, broker/stats/irc 100%)
- Binary builds — pass
### Rebase Integrity
✅ No conflict markers found. Clean 2-commit stack on top of main (db3d23c from PR #82).
### Requirements Checklist ([#83](https://git.eeqj.de/sneak/chat/issues/83))
| # | Requirement | Status |
|---|-----------|--------|
| 1 | Remove `/register` endpoint and `RegisterUser` DB method | ✅ Route removed from `routes.go`, `HandleRegister` replaced with `HandleLogin`, `RegisterUser` replaced with `SetPassword` |
| 2 | HttpOnly cookies (HttpOnly, Secure, SameSite=Strict) | ✅ `setAuthCookie` sets all three; `Secure` conditional on TLS/X-Forwarded-Proto |
| 3 | `POST /api/v1/session` creates session + sets cookie | ✅ Token removed from JSON body, cookie set via `setAuthCookie` |
| 4 | `PASS` IRC command for password setting (min 8 chars, bcrypt) | ✅ `handlePass` in `auth.go`, `SetPassword` in `db/auth.go`, `CmdPass` constant added |
| 5 | `POST /api/v1/login` sets cookie for additional clients | ✅ Cookie set, token removed from JSON response, channel state initialization added |
| 6 | CORS: `AllowCredentials: true` | ✅ `AllowOriginFunc` reflects origin, `AllowCredentials: true`, `Authorization` removed from `AllowedHeaders` |
| 7 | Logout and QUIT clear cookie | ✅ `clearAuthCookie` called in both `HandleLogout` and `handleQuit` |
| 8 | CLI updated with cookie jar | ✅ `cookiejar.New()` in `NewClient`, `Token` field removed, `Authorization` header removed from `do()` and `PollMessages` |
| 9 | README updated | ✅ Extensive rewrite — see doc accuracy section below |
### CRITICAL: Documentation Accuracy Check
**"Clients never need to handle the cookie" false claim:** ✅ NOT PRESENT. Grep for `never need to handle`, `never handle`, `clients never` — zero matches. The rework fix survived the rebase.
**Remaining `Bearer` / `Authorization` references in README:** ✅ None found (only the TODO line mentioning "cookies replace Bearer tokens" which is correct).
**Remaining `Bearer` / `Authorization` references in Go code:** ✅ None found.
**Remaining `/register` or `RegisterUser` references in Go code:** ✅ None found.
**Cookie handling accuracy in README:** ✅ Multiple passages correctly state CLI clients MUST manage cookies (e.g., curl `-c`/`-b` flags, Python `requests.Session`, etc.).
### ❌ BLOCKING: Stale `register`/`registration` references in README
Three README passages still reference the removed registration flow:
1. **Line 215**: `"username field in the session/register request"` — The `/register` endpoint no longer exists. Should say `"session request"` or `"session creation request"`.
2. **Line 221**: `"created at session creation, registration, or login"` — Registration is no longer a thing. Should say `"session creation or login"`.
3. **Line 2471**: `"on session creation, registration, or NICK change"` — HTTP 409 cannot occur on registration anymore. Should say `"on session creation or NICK change"`.
These are documentation inaccuracies — they describe a flow that no longer exists in the codebase.
### Code Quality Notes (non-blocking)
- Clean separation of concerns: `setAuthCookie`/`clearAuthCookie` helpers are well-structured
- CORS `AllowOriginFunc` correctly reflects origin (required for `AllowCredentials: true`)
- Test coverage is solid — new `TestSessionCookie` verifies HttpOnly + SameSite properties, `TestPassCommand*` tests cover happy path, short password, and empty body
- `TestLoginValid` correctly extracts cookie from response instead of JSON body
- `handlePass` uses `respondIRCError` with `ErrNeedMoreParams` (461) for IRC-level errors, consistent with other command handlers
### REPO_POLICIES Compliance
- ✅ All external references pinned by hash (Dockerfile base images)
- ✅ Makefile targets present and functional
- ✅ `make check` passes without modifying files
- ✅ No secrets committed
- ✅ No `.golangci.yml` modifications
- ✅ No `git add -A` or `git add .`
---
### Verdict: ❌ FAIL
**Reason:** 3 stale documentation references to the removed `/register` flow survive in README.md (lines 215, 221, 2471). Documentation inaccuracies are blocking per review policy.
**To pass:** Replace `"session/register request"` → `"session request"`, `"session creation, registration, or login"` → `"session creation or login"`, `"session creation, registration, or NICK change"` → `"session creation or NICK change"`. Everything else is clean.
The /register endpoint was removed in this PR but three references
to it survived in README.md:
- 'session/register request' → 'session request'
- 'session creation, registration, or login' → 'session creation or login'
- 'session creation, registration, or NICK change' → 'session creation or NICK change'
Rework complete — removed 3 stale registration references from README.md:
Line ~215: session/register request → session request
Line ~221: session creation, registration, or login → session creation or login
Line ~2471: session creation, registration, or NICK change → session creation or NICK change
Grepped for all remaining register/registration occurrences — the rest are either IRC terminology ("connection registration"), philosophy statements ("no registration"), or assertions that nick registration doesn't exist. All correct.
make fmt✅ | docker build .✅ (lint clean, all tests pass)
**Rework complete** — removed 3 stale registration references from README.md:
1. Line ~215: `session/register request` → `session request`
2. Line ~221: `session creation, registration, or login` → `session creation or login`
3. Line ~2471: `session creation, registration, or NICK change` → `session creation or NICK change`
Grepped for all remaining `register`/`registration` occurrences — the rest are either IRC terminology ("connection registration"), philosophy statements ("no registration"), or assertions that nick registration doesn't exist. All correct.
`make fmt` ✅ | `docker build .` ✅ (lint clean, all tests pass)
Commit: [`9e8d39a`](https://git.eeqj.de/sneak/chat/commit/9e8d39a)
APPROVED. The rework correctly addressed all three stale references. No regressions.
## Final Review — PASS ✅
**Scope:** Verifying the 3 stale "registration" references in README.md were fixed per [previous review](https://git.eeqj.de/sneak/chat/pulls/84).
### Fixes Verified
All three stale references to the removed `/register` endpoint are corrected:
1. ~~"session/register request"~~ → **"session request"** (line 215) ✅
2. ~~"session creation, registration, or login"~~ → **"session creation or login"** (line 221) ✅
3. ~~"session creation, registration, or NICK change"~~ → **"session creation or NICK change"** (line 2471) ✅
### Remaining "regist" occurrences — all legitimate
Grepped README.md for all case-insensitive `regist` matches. 8 remaining occurrences, all referring to general concepts (not the removed endpoint):
- L155: "no separate registration step" — describes the auth flow philosophy
- L249: "no nick registration or reservation system" — nick policy
- L355: "persistent channel registration" — channel policy
- L990: "connection registration" — IRC protocol term (RFC 2812)
- L2503: "no account registration" — anti-abuse philosophy
- L2600: "No registration, no email" — design philosophy
- L2643: "Connection registration numerics" — IRC protocol checklist item
- L2768: "No registration, no email verification" — design philosophy
None of these reference the removed `/api/v1/session/register` endpoint.
### Build
`docker build .` — **PASS**. fmt-check clean, lint 0 issues, all tests pass.
### Verdict
**APPROVED.** The rework correctly addressed all three stale references. No regressions.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Major auth refactor replacing Bearer token authentication with HttpOnly cookie-based auth, removing the registration endpoint, and adding the PASS IRC command for password management.
Changes
Removed
POST /api/v1/registerendpoint (no separate registration path)RegisterUserDB methodAuthorization: Bearerheader parsingtokenfield from all JSON response bodiesTokenfield from CLISessionResponsetypeAdded
neoirc_authHttpOnly cookie set on session creation and loginPOST /api/v1/messages {"command":"PASS","body":["password"]}(minimum 8 characters)SetPasswordDB method (bcrypt hashing)setAuthCookie(),clearAuthCookie()AllowCredentials: truewith origin reflection functionAuth Flow
POST /api/v1/session {"nick":"alice"}→ setsneoirc_authcookie, returns{"id":1,"nick":"alice"}POST /api/v1/messages {"command":"PASS","body":["s3cret"]}→ sets password for multi-clientPOST /api/v1/login {"nick":"alice","password":"s3cret"}→ setsneoirc_authcookieTests
TestPassCommand,TestPassCommandShortPassword,TestPassCommandEmpty,TestSessionCookieREADME
CI
docker build .passes (format check, lint, all tests, build)closes sneak/chat#83
Review: PR #84 — Auth Refactor (Bearer tokens → HttpOnly cookies)
Verdict: PASS ✅
Verification Points
setAuthCookie()correctly sets all three.Securechecks bothrequest.TLSandX-Forwarded-Protofor reverse proxy support.clearAuthCookie()mirrors the same properties withMaxAge: -1.tokenin JSON responseshandleCreateSessionandhandleLoginboth return only{"id", "nick"}.SessionResponsetype in CLI also has onlyID+Nick. TestTestSessionCookieexplicitly asserts notokenkey exists in the JSON body.handlePassvalidates empty/missing body → ERR_NEEDMOREPARAMS (461), short password (<8) → ERR_NEEDMOREPARAMS, then callsdatabase.SetPassword()which usesbcrypt.GenerateFromPasswordat default cost. Three tests cover this:TestPassCommand,TestPassCommandShortPassword,TestPassCommandEmpty./registerroute gone fromroutes.go.HandleRegisterfunction removed.RegisterUserDB method removed.grepconfirms no register references remain (only IRC numerics).grep -rn "Bearer|Authorization" --include="*.go"returns nothing.authSession()now reads fromrequest.Cookie(authCookieName).Authorizationremoved from CORSAllowedHeaders.AllowCredentials: trueAllowOriginFunc(reflects origin, not*),AllowCredentials: true.handleQuitandHandleLogoutcallclearAuthCookie()which setsMaxAge: -1.Client.Tokenfield removed.NewClient()createscookiejar.New(nil)and setsJaronHTTPClient.PollMessagesshares the jar.do()no longer sets Authorization header.-c/-b cookies.txt), Python/JS examples, security model, roadmap.doRequestAuthuses cookies.createSessionextracts cookie from response. Register tests replaced with PASS command tests. NewTestSessionCookievalidates HttpOnly, SameSite, and no-token-in-body.Build Result
Code Quality Notes
Positive:
setAuthCookie/clearAuthCookie) reused consistently across session creation, login, logout, and QUITinitChannelState()Minor Documentation Nits (non-blocking)
A few stale
tokenreferences survived in README sections adjacent to or near changed lines:token_a, token_b, token_c— could becookie_aetc.None affect functionality. Worth a follow-up cleanup.
it says “clients never need to handle the cookie” - this is false. this is how cli clients will need to auth to the api.
those documentation inaccuracies aren’t nits - they are failures
pipeline manager, you should have caught this.
Rework: Fixed ALL documentation inaccuracies about cookie-based auth
Addressing sneak's review feedback on PR #84 (closes #83).
Fixes Applied
CRITICAL — "clients never need to handle the token directly" (line ~165): Replaced this false claim with accurate text: "Browsers handle cookies automatically. CLI clients (curl, custom HTTP clients) must explicitly save and send cookies — e.g., using curl's
-c/-bflags or an HTTP cookie jar in their language's HTTP library.""Clients never handle tokens directly" (line ~367, JWT comparison section): Same false claim repeated. Fixed to: "Browsers and HTTP cookie jars manage cookies automatically; CLI clients must explicitly save and resend cookies (e.g., curl
-c/-bflags)"Rationale section (line ~199): Fixed "eliminates token management from client code entirely" → "simplifies credential management — browsers handle cookies automatically, and CLI clients just need a cookie jar"
Multi-client diagram (lines ~237-239):
token_a, token_b, token_c→cookie_a, cookie_b, cookie_cSet-Cookie placeholders in diagrams (lines ~398, 431, 447):
<token>→<random_hex>,<token_a>→<cookie_a>,<token_b>→<cookie_b>QUIT command description (line ~834): "old token" → "old auth cookie"
Client Development Guide (line ~2231): "→ get token" → "→ get auth cookie"
Security Model (line ~1836): "Tokens are hashed (SHA-256) before storage" → "Cookie values are hashed (SHA-256) before storage"
Session cleanup (line ~2087): "Individual client tokens are deleted" → "Individual client auth cookies are invalidated"
Session Creation (line ~162-163): "containing a cryptographically random token" → "containing a cryptographically random value"; "No token appears in the JSON body" → "No auth credential appears in the JSON body"
Common Properties (line ~191): "encoded in the token" → "encoded in the cookie value"
Protocol diagram (line ~406): "Cookie sent automatically on all subsequent requests" → "Cookie must be sent on all subsequent requests"
API Reference POST /api/v1/session (line ~1154-1155): "containing the auth token. The JSON body does not include the token" → "containing an opaque auth value. The JSON body does not include the auth credential"
Database schema (line ~2019): Updated
tokencolumn description from "Unique auth token" → "Auth cookie value" (column name kept as-is since it matches the actual code)Verified Non-Issues ("token" references that are correct)
pow_token/ hashcash proof-of-work — not auth tokenstoken— matches actual code, kept as-isBuild Status
make fmt✅ passesdocker build .❌ pre-existing test timeout ininternal/handlers(30s timeout) — confirmed identical failure on the branch before this commit (README-only changes cannot affect tests)Review: PR #84 — Auth Refactor (post-rework — doc fixes)
Verdict: PASS ✅
1. sneak's Comments — Verification
2. Documentation Accuracy Verification
Exhaustive grep of README.md for stale references:
grep -i 'never need to handle\|never handle\|clients never'→ zero matches ✅grep -i 'Bearer'→ 1 match: line 2565 in completed TODO item[x] Cookie-based auth — HttpOnly cookies replace Bearer tokens— historically accurate, correct ✅grep -i '/register'→ zero matches ✅grep -i 'Authorization'→ zero matches in README or Go source ✅All
tokenreferences in README verified non-stale:pow_token(hashcash proof-of-work) — 7 occurrences, all correcttokencolumn — documented as "Auth cookie value (SHA-256 hash of the 64-hex-char cookie)"token buckets(rate limiting term) — unrelated to authPrevious review's "nits" verified fixed:
"old token" (line→ now "old auth cookie" ✅834)"→ get token" (line→ now "→ get auth cookie" ✅2231)→ nowtoken_a, token_b, token_c(lines237-239)cookie_a, cookie_b, cookie_c✅3. Requirements Checklist
POST /api/v1/registerroutes.go, handler removed fromauth.go,RegisterUserDB method removedsetAuthCookie()sets all three;Securechecksrequest.TLSandX-Forwarded-ProtoPOST /api/v1/sessionsets cookiehandleCreateSessioncallssetAuthCookie, JSON body has no tokenhandlePassvalidates length,SetPasswordusesbcrypt.GenerateFromPasswordPOST /api/v1/loginsets cookiehandleLogincallssetAuthCookie, JSON body has no tokenAllowCredentials: true+ origin reflectionAllowOriginFuncreturns true,AllowCredentials: true,Authorizationremoved from headersclearAuthCookie()withMaxAge: -1cookiejar.New(nil)inNewClient(),Tokenfield removed, noAuthorizationheader-c/-b), Python/JS examples, security model, roadmap4. Policy Compliance
.golangci.ymlmodifications ✅TestSessionCookievalidates HttpOnly, SameSite, no-token-in-body) ✅make fmt-check,make lint,make testall pass ✅5. Build Result
6. Merge Note
⚠️ Rebase required before merge. The PR branch is based on
bf4d63bbutmainhas advanced todb3d23c(PR #82). Merge conflicts exist in 6 files:README.md,internal/db/auth.go,internal/db/auth_test.go,internal/handlers/api.go,internal/handlers/auth.go,pkg/irc/commands.go. The code is correct; it just needs to be rebased onto currentmain.3283a72050to301fa9971f301fa9971fto61aa678492Rebase Complete
Rebased
feature/cookie-auth-refactoronto currentmain(db3d23c) which includes PR #82 (username/hostname/OPER support).Conflicts Resolved
pkg/irc/commands.go— kept bothCmdOper(PR #82) andCmdPass(PR #84)internal/db/auth.go— removedRegisterUser(PR #84's intent), keptSetPasswordwith correct signature; preservedSetBcryptCostandLoginUserwith IP/hostname params from PR #82internal/db/auth_test.go— updated all tests to useCreateSession(ctx, nick, "", "", "")(PR #82's 5-arg signature) +SetPasswordpattern instead of removedRegisterUser; removed PR #82'sRegisterUser-specific testsinternal/handlers/auth.go— removedHandleRegister(PR #84's intent), keptHandleLoginwith cookie auth and IP/hostname resolutioninternal/handlers/api.go— keptclientIPandresolveHostnameutilities from PR #82; kept cookie-based auth from PR #84; fixedpayload.Nick→nickinexecuteCreateSession(variable not in scope after conflict resolution)internal/handlers/api_test.go— updatedcreateSessionWithUsernamehelper to extract auth cookie instead of JSON token (matching PR #84's cookie-based auth)README.md— removed/registerendpoint docs (PR #84); merged clients table schema to include both cookie description (PR #84) andip/hostnamecolumns (PR #82)Verification
make fmt✅docker build .✅ (lint, fmt-check, all tests pass)Review: PR #84 — Cookie-Based Auth Refactor (post-rebase)
Reviewer: clawbot (automated)
Branch:
feature/cookie-auth-refactor(2 commits onmain@db3d23c)Closes: #83
Build Result
✅
docker build .— PASSmake fmt-check— passmake lint— 0 issuesmake test— all packages pass (handlers 66.6% coverage, db 51.6%, broker/stats/irc 100%)Rebase Integrity
✅ No conflict markers found. Clean 2-commit stack on top of main (
db3d23cfrom PR #82).Requirements Checklist (#83)
/registerendpoint andRegisterUserDB methodroutes.go,HandleRegisterreplaced withHandleLogin,RegisterUserreplaced withSetPasswordsetAuthCookiesets all three;Secureconditional on TLS/X-Forwarded-ProtoPOST /api/v1/sessioncreates session + sets cookiesetAuthCookiePASSIRC command for password setting (min 8 chars, bcrypt)handlePassinauth.go,SetPasswordindb/auth.go,CmdPassconstant addedPOST /api/v1/loginsets cookie for additional clientsAllowCredentials: trueAllowOriginFuncreflects origin,AllowCredentials: true,Authorizationremoved fromAllowedHeadersclearAuthCookiecalled in bothHandleLogoutandhandleQuitcookiejar.New()inNewClient,Tokenfield removed,Authorizationheader removed fromdo()andPollMessagesCRITICAL: Documentation Accuracy Check
"Clients never need to handle the cookie" false claim: ✅ NOT PRESENT. Grep for
never need to handle,never handle,clients never— zero matches. The rework fix survived the rebase.Remaining
Bearer/Authorizationreferences in README: ✅ None found (only the TODO line mentioning "cookies replace Bearer tokens" which is correct).Remaining
Bearer/Authorizationreferences in Go code: ✅ None found.Remaining
/registerorRegisterUserreferences in Go code: ✅ None found.Cookie handling accuracy in README: ✅ Multiple passages correctly state CLI clients MUST manage cookies (e.g., curl
-c/-bflags, Pythonrequests.Session, etc.).❌ BLOCKING: Stale
register/registrationreferences in READMEThree README passages still reference the removed registration flow:
Line 215:
"username field in the session/register request"— The/registerendpoint no longer exists. Should say"session request"or"session creation request".Line 221:
"created at session creation, registration, or login"— Registration is no longer a thing. Should say"session creation or login".Line 2471:
"on session creation, registration, or NICK change"— HTTP 409 cannot occur on registration anymore. Should say"on session creation or NICK change".These are documentation inaccuracies — they describe a flow that no longer exists in the codebase.
Code Quality Notes (non-blocking)
setAuthCookie/clearAuthCookiehelpers are well-structuredAllowOriginFunccorrectly reflects origin (required forAllowCredentials: true)TestSessionCookieverifies HttpOnly + SameSite properties,TestPassCommand*tests cover happy path, short password, and empty bodyTestLoginValidcorrectly extracts cookie from response instead of JSON bodyhandlePassusesrespondIRCErrorwithErrNeedMoreParams(461) for IRC-level errors, consistent with other command handlersREPO_POLICIES Compliance
make checkpasses without modifying files.golangci.ymlmodificationsgit add -Aorgit add .Verdict: ❌ FAIL
Reason: 3 stale documentation references to the removed
/registerflow survive in README.md (lines 215, 221, 2471). Documentation inaccuracies are blocking per review policy.To pass: Replace
"session/register request"→"session request","session creation, registration, or login"→"session creation or login","session creation, registration, or NICK change"→"session creation or NICK change". Everything else is clean.Rework complete — removed 3 stale registration references from README.md:
session/register request→session requestsession creation, registration, or login→session creation or loginsession creation, registration, or NICK change→session creation or NICK changeGrepped for all remaining
register/registrationoccurrences — the rest are either IRC terminology ("connection registration"), philosophy statements ("no registration"), or assertions that nick registration doesn't exist. All correct.make fmt✅ |docker build .✅ (lint clean, all tests pass)Commit:
9e8d39aFinal Review — PASS ✅
Scope: Verifying the 3 stale "registration" references in README.md were fixed per previous review.
Fixes Verified
All three stale references to the removed
/registerendpoint are corrected:"session/register request"→ "session request" (line 215) ✅"session creation, registration, or login"→ "session creation or login" (line 221) ✅"session creation, registration, or NICK change"→ "session creation or NICK change" (line 2471) ✅Remaining "regist" occurrences — all legitimate
Grepped README.md for all case-insensitive
registmatches. 8 remaining occurrences, all referring to general concepts (not the removed endpoint):None of these reference the removed
/api/v1/session/registerendpoint.Build
docker build .— PASS. fmt-check clean, lint 0 issues, all tests pass.Verdict
APPROVED. The rework correctly addressed all three stale references. No regressions.