oficina / memoria Public

Home mode: a signed-out page request carries return_to

The local plugin's RequireSession redirects a signed-out page request to
/signin with no return_to, so after Home sign-in Memoria landed on the
root instead of the page the person asked for. In Home mode, redirect
GET/HEAD to /signin?return_to=<request URI>; connect's Signin keeps a
safe path through the exchange and lands on it.

TestHomeSigninReturnsToTheExactPage failed first (/connectors went to
"/signin") and passes now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 6030871d970d189110e6d48d948f2756b0f4292e parent e504bca
2 files changed, +37 −0
  • internal/memoria/app.go +12 −0
  • internal/memoria/home_consumer_test.go +25 −0

CI passed — run details

CI log
=== vet ===
go vet ./...
=== fmt ===
=== test ===
go test ./...
?   	amadan.net/oficina/memoria/cmd/memoria	[no test files]
ok  	amadan.net/oficina/memoria/internal/memoria	8.711s
ok  	amadan.net/oficina/memoria/internal/memoria/chunk	(cached)
ok  	amadan.net/oficina/memoria/internal/memoria/fact	(cached)
?   	amadan.net/oficina/memoria/internal/memoria/icons	[no test files]
ok  	amadan.net/oficina/memoria/internal/memoria/index	(cached)
ok  	amadan.net/oficina/memoria/internal/memoria/notion	(cached)
ok  	amadan.net/oficina/memoria/internal/memoria/provider	(cached)
ok  	amadan.net/oficina/memoria/internal/memoria/query	(cached)
ok  	amadan.net/oficina/memoria/internal/memoria/seal	(cached)
ok  	amadan.net/oficina/memoria/internal/memoria/sibling	(cached)
ok  	amadan.net/oficina/memoria/internal/memoriatest	5.044s
?   	amadan.net/oficina/memoria/scripts/writeestilo	[no test files]
?   	amadan.net/oficina/memoria/tools/bench	[no test files]
?   	amadan.net/oficina/memoria/tools/drive	[no test files]
?   	amadan.net/oficina/memoria/tools/seed	[no test files]
?   	amadan.net/oficina/memoria/tools/vendorassets	[no test files]
=== migration-check ===
go run github.com/carlosframework/rastrillo/cmd/rastrillo migration check
rastrillo migration check: models and migrations agree
diff --git a/internal/memoria/app.go b/internal/memoria/app.go
index b2e3501..ef42cef 100644
--- a/internal/memoria/app.go
+++ b/internal/memoria/app.go
@@ -6,6 +6,7 @@ import (
"context"
"log/slog"
"net/http"
+ "net/url"
"strings"
"github.com/go-chi/chi/v5"
@@ -235,6 +236,17 @@ func (a *app) requireSession(next http.Handler) http.Handler {
}
next.ServeHTTP(w, r)
})
+ // In Home mode a signed-out page request carries the page to
+ // sign-in, so Home sign-in comes back to it rather than to the
+ // root: connect's Signin keeps a safe return_to through the
+ // exchange. The local plugin's own refusal drops the path. Same
+ // cookie, same table: WireHome checked the names agree.
+ if a.home != nil && (r.Method == http.MethodGet || r.Method == http.MethodHead) {
+ if _, ok := a.home.sessions.From(r); !ok {
+ http.Redirect(w, r, signinPath+"?return_to="+url.QueryEscape(r.URL.RequestURI()), http.StatusSeeOther)
+ return
+ }
+ }
a.auth.RequireSession(guarded).ServeHTTP(w, r)
})
}
diff --git a/internal/memoria/home_consumer_test.go b/internal/memoria/home_consumer_test.go
index 5b35997..5c41319 100644
--- a/internal/memoria/home_consumer_test.go
+++ b/internal/memoria/home_consumer_test.go
@@ -90,3 +90,28 @@ func TestAllAppsLinksToDirectory(t *testing.T) {
t.Fatal("All apps shown with no directory configured")
}
}
+
+// A signed-out visit to a page in Home mode goes to sign-in carrying the
+// page, and Home sign-in comes back to that exact page, not the root.
+func TestHomeSigninReturnsToTheExactPage(t *testing.T) {
+ f := newHomeConsumerFixture(t, "")
+ f.local(t)
+ if w := f.do("GET", "/account/home", ""); w.Code != http.StatusOK {
+ t.Fatal("link page", w.Code)
+ }
+ start := homeRedirect(t, f.do("POST", "/account/home", ""))
+ if homeRedirect(t, f.exchange(t, start, "opaque-home-alice")) != "/account/home/confirm" {
+ t.Fatal("wrong proof landing")
+ }
+ homeRedirect(t, f.do("POST", "/account/home/confirm", ""))
+ f.do("POST", "/signout", "")
+
+ signin := homeRedirect(t, f.do("GET", "/connectors", ""))
+ if signin != "/signin?return_to=%2Fconnectors" {
+ t.Fatalf("signed-out /connectors went to %q, want /signin?return_to=%%2Fconnectors", signin)
+ }
+ start = homeRedirect(t, f.do("GET", signin, ""))
+ if got := homeRedirect(t, f.exchange(t, start, "opaque-home-alice")); got != "/connectors" {
+ t.Fatalf("Home sign-in landed on %q, want /connectors", got)
+ }
+}