internal/server/routes.gosetupUserRoutes() applies CSRF() to /user/{username} but not RequireAuth(). Authentication is enforced only inside HandleProfile (internal/handlers/profile.go), which is defense-by-implementation rather than defense-by-design. Every other authenticated route group (/sources, /source/{sourceID}) is wrapped in s.mw.RequireAuth(); this one should be too.
Definition of done:
the /user/{username} route group uses s.mw.RequireAuth()
an unauthenticated request is redirected to /pages/login at the middleware layer (not only by the handler)
existing profile behaviour (own-profile-only) and its tests still pass
Part of the road to 1.0 (see #33).
`internal/server/routes.go` `setupUserRoutes()` applies `CSRF()` to `/user/{username}` but not `RequireAuth()`. Authentication is enforced only inside `HandleProfile` (`internal/handlers/profile.go`), which is defense-by-implementation rather than defense-by-design. Every other authenticated route group (`/sources`, `/source/{sourceID}`) is wrapped in `s.mw.RequireAuth()`; this one should be too.
Definition of done:
- the `/user/{username}` route group uses `s.mw.RequireAuth()`
- an unauthenticated request is redirected to `/pages/login` at the middleware layer (not only by the handler)
- existing profile behaviour (own-profile-only) and its tests still pass
clawbot
added this to the 1.0.0 milestone 2026-08-07 13:10:51 +02:00
The /user/{username} route group currently has only r.Use(s.mw.CSRF()). Add r.Use(s.mw.RequireAuth()) immediately after the CSRF line, so auth is enforced at the middleware layer — matching /sources and /source/{sourceID}, which apply CSRF then RequireAuth. Keep the relative order: CSRF first, then RequireAuth.
With RequireAuth in front of the route, the handler's own session.Get + IsAuthenticated + redirect-to-login branch is now dead code (the middleware guarantees an authenticated session before the handler runs). Simplify it:
keep reading the username and user id from the session — still needed for the own-profile-only comparison (requestedUsername != sessionUsername) and the template data
remove the now-unreachable "not authenticated -> redirect to /pages/login" branch
do NOT change the own-profile-only behaviour: a request for another user's profile must still return 403
Tests
keep/adjust the internal/handlers profile tests: own profile returns 200, another user's profile returns 403
add a test proving an unauthenticated request to /user/{username} is redirected to /pages/login by the middleware and never reaches the handler (a routing- or middleware-level test is fine)
Validation gates
run make fmt before committing
host Go is 1.25 but go.mod requires 1.26, so validate with the authoritative CI gate: docker build . must pass (it runs fmt-check, lint, test, and build). Do not skip lint; no new lint findings.
Process
branch from main, named issue-60-user-requireauth
the commit subject must end with (closes #60)
do not reference any AI assistant or tooling anywhere (no trailers, no mentions)
open a PR with base main, then post a comment on the PR describing exactly what changed and pasting the docker build . result
## Implementation instructions
Scope is exactly the definition of done above — one small, isolated change. Do not expand scope.
### Change 1 — `internal/server/routes.go`, `setupUserRoutes()`
The `/user/{username}` route group currently has only `r.Use(s.mw.CSRF())`. Add `r.Use(s.mw.RequireAuth())` immediately after the CSRF line, so auth is enforced at the middleware layer — matching `/sources` and `/source/{sourceID}`, which apply CSRF then RequireAuth. Keep the relative order: CSRF first, then RequireAuth.
### Change 2 — `internal/handlers/profile.go`, `HandleProfile`
With `RequireAuth` in front of the route, the handler's own `session.Get` + `IsAuthenticated` + redirect-to-login branch is now dead code (the middleware guarantees an authenticated session before the handler runs). Simplify it:
- keep reading the username and user id from the session — still needed for the own-profile-only comparison (`requestedUsername != sessionUsername`) and the template data
- remove the now-unreachable "not authenticated -> redirect to `/pages/login`" branch
- do NOT change the own-profile-only behaviour: a request for another user's profile must still return 403
### Tests
- keep/adjust the `internal/handlers` profile tests: own profile returns 200, another user's profile returns 403
- add a test proving an unauthenticated request to `/user/{username}` is redirected to `/pages/login` by the middleware and never reaches the handler (a routing- or middleware-level test is fine)
### Validation gates
- run `make fmt` before committing
- host Go is 1.25 but `go.mod` requires 1.26, so validate with the authoritative CI gate: `docker build .` must pass (it runs fmt-check, lint, test, and build). Do not skip lint; no new lint findings.
### Process
- branch from `main`, named `issue-60-user-requireauth`
- the commit subject must end with ` (closes #60)`
- do not reference any AI assistant or tooling anywhere (no trailers, no mentions)
- open a PR with base `main`, then post a comment on the PR describing exactly what changed and pasting the `docker build .` result
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.
Part of the road to 1.0 (see #33).
internal/server/routes.gosetupUserRoutes()appliesCSRF()to/user/{username}but notRequireAuth(). Authentication is enforced only insideHandleProfile(internal/handlers/profile.go), which is defense-by-implementation rather than defense-by-design. Every other authenticated route group (/sources,/source/{sourceID}) is wrapped ins.mw.RequireAuth(); this one should be too.Definition of done:
/user/{username}route group usess.mw.RequireAuth()/pages/loginat the middleware layer (not only by the handler)Implementation instructions
Scope is exactly the definition of done above — one small, isolated change. Do not expand scope.
Change 1 —
internal/server/routes.go,setupUserRoutes()The
/user/{username}route group currently has onlyr.Use(s.mw.CSRF()). Addr.Use(s.mw.RequireAuth())immediately after the CSRF line, so auth is enforced at the middleware layer — matching/sourcesand/source/{sourceID}, which apply CSRF then RequireAuth. Keep the relative order: CSRF first, then RequireAuth.Change 2 —
internal/handlers/profile.go,HandleProfileWith
RequireAuthin front of the route, the handler's ownsession.Get+IsAuthenticated+ redirect-to-login branch is now dead code (the middleware guarantees an authenticated session before the handler runs). Simplify it:requestedUsername != sessionUsername) and the template data/pages/login" branchTests
internal/handlersprofile tests: own profile returns 200, another user's profile returns 403/user/{username}is redirected to/pages/loginby the middleware and never reaches the handler (a routing- or middleware-level test is fine)Validation gates
make fmtbefore committinggo.modrequires 1.26, so validate with the authoritative CI gate:docker build .must pass (it runs fmt-check, lint, test, and build). Do not skip lint; no new lint findings.Process
main, namedissue-60-user-requireauth(closes #60)main, then post a comment on the PR describing exactly what changed and pasting thedocker build .result