1 Commits
Author SHA1 Message Date
sneak 30cccebf32 Logging in returns to the page that was asked for (closes #384)
check / check (push) Failing after 5m2s
RequireAuth now sends a logged-out GET to /pages/login with its path
and query in a `next` parameter. The login form carries it as a hidden
field, and a successful login redirects there when it is a path on
this site; anything else, plain or percent-encoded, goes to `/`, which
leads to the webhook list. A browser already logged in that opens the
login page goes to the same place. The navigation bar on the login
page no longer links to the login page.

Model: opus-5-5
2026-10-01 20:45:57 +00:00
4 changed files with 11 additions and 21 deletions
+3 -3
View File
@@ -2698,9 +2698,9 @@ abuse limit later; they are tracked as future work.
#### Authenticated Endpoints #### Authenticated Endpoints
A logged-out `GET` of any of these is redirected to `/pages/login` with A logged-out request to any of these is redirected to `/pages/login`. A
its path and query as `next`, so logging in returns to the page that was `GET` carries its path and query there as `next`, so logging in returns
asked for. to the page that was asked for.
| Method | Path | Description | | Method | Path | Description |
| ------ | ------------------------ | ----------- | | ------ | ------------------------ | ----------- |
+4 -16
View File
@@ -539,8 +539,7 @@ func loginPageGet(
// TestLoginPage_CarriesOnlyAPathOnThisSite covers the login page // TestLoginPage_CarriesOnlyAPathOnThisSite covers the login page
// itself: its form carries the requested page only when it is a path // itself: its form carries the requested page only when it is a path
// on this site, and a browser already logged in goes straight there, // on this site, and a browser already logged in goes straight there.
// or to "/" when it is not.
func TestLoginPage_CarriesOnlyAPathOnThisSite(t *testing.T) { func TestLoginPage_CarriesOnlyAPathOnThisSite(t *testing.T) {
t.Parallel() t.Parallel()
@@ -564,21 +563,10 @@ func TestLoginPage_CarriesOnlyAPathOnThisSite(t *testing.T) {
) )
cookies := authenticatedCookies(t, sess, "test-user-id", "testuser") cookies := authenticatedCookies(t, sess, "test-user-id", "testuser")
w := loginPageGet(h, "/source/abc", cookies)
cases := []struct{ next, want string }{ assert.Equal(t, http.StatusSeeOther, w.Code)
{"/source/abc", "/source/abc"}, assert.Equal(t, "/source/abc", w.Header().Get("Location"))
{"//evil.example/", "/"},
{`/\evil.example/`, "/"},
}
for _, c := range cases {
w := loginPageGet(h, c.next, cookies)
assert.Equal(t, http.StatusSeeOther, w.Code, "next %q", c.next)
assert.Equal(
t, c.want, w.Header().Get("Location"), "next %q", c.next,
)
}
} }
// TestLoginPage_HasNoLinkToItself: the navigation bar on the login // TestLoginPage_HasNoLinkToItself: the navigation bar on the login
-2
View File
@@ -88,8 +88,6 @@ func TestHandleProfile_OwnProfile_OK(t *testing.T) {
h.HandleProfile().ServeHTTP(w, req) h.HandleProfile().ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
assert.Contains(t, w.Body.String(), "Account Information")
assert.NotContains(t, w.Body.String(), "Account Type")
} }
func TestHandleProfile_OtherProfile_Forbidden(t *testing.T) { func TestHandleProfile_OtherProfile_Forbidden(t *testing.T) {
+4
View File
@@ -41,6 +41,10 @@
<dt class="w-32 text-sm font-medium text-gray-500">Username</dt> <dt class="w-32 text-sm font-medium text-gray-500">Username</dt>
<dd class="text-sm text-gray-900">{{.User.Username}}</dd> <dd class="text-sm text-gray-900">{{.User.Username}}</dd>
</div> </div>
<div class="flex">
<dt class="w-32 text-sm font-medium text-gray-500">Account Type</dt>
<dd class="text-sm text-gray-900">Standard User</dd>
</div>
</dl> </dl>
</div> </div>
</div> </div>