Pin home/connect to Home main (7a0f19c): in-app sign-out chains to id.*
connect's Signout now hands the POST on to {Issuer}/signout as a 307
(shared-chrome spec §9.5), and Signin skips the exchange for a live
signed-in session. The Home router tests asserted the old 303; they now
assert the chain itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 files changed,
+18
−7
go.mod+1 −1go.sum+2 −0internal/calls/home_release_test.go+1 −1internal/calls/home_router_test.go+13 −4internal/calls/native_auth_test.go+1 −1
diff --git a/go.mod b/go.mod| index 7fbf2ec..3ae112d 100644 |
| --- a/go.mod |
| +++ b/go.mod |
| @@ -4,7 +4,7 @@ go 1.26.0 |
| require ( |
| amadan.net/oficina/estilo.git v0.0.0-20260929231226-cce11010ece0 |
| - amadan.net/oficina/home/connect v0.0.0-20260910000424-5b8d8410775f |
| + amadan.net/oficina/home/connect v0.0.0-20260930200947-7a0f19c3a8a7 |
| amadan.net/oficina/home/handoff v0.0.0-20260908102137-481d2d57f489 |
| amadan.net/rastrillo/rastrillo v0.26.1-0.20260908075707-29e4491631bd |
| github.com/go-chi/chi/v5 v5.3.2 |
diff --git a/go.sum b/go.sum| index c6810c4..5b1e238 100644 |
| --- a/go.sum |
| +++ b/go.sum |
| @@ -2,6 +2,8 @@ amadan.net/oficina/estilo.git v0.0.0-20260929231226-cce11010ece0 h1:Qww+xGIYJaj+ |
| amadan.net/oficina/estilo.git v0.0.0-20260929231226-cce11010ece0/go.mod h1:4u0jNBtIdMLOsJyrScfqoEuYDJF7BBM3qM9YWbzt6aw= |
| amadan.net/oficina/home/connect v0.0.0-20260910000424-5b8d8410775f h1:lkw3tjzHfZxe74pNfo+tGDz4mkZLg+D6R+FTkmsym6M= |
| amadan.net/oficina/home/connect v0.0.0-20260910000424-5b8d8410775f/go.mod h1:YroavXe43Wietec+po8mtSk4a9Bir2h31Q3iAf4MWxI= |
| +amadan.net/oficina/home/connect v0.0.0-20260930200947-7a0f19c3a8a7 h1:FVzfiViGRUtl/enMtf34LL2ECLCLJx5V2JJmsOxuOEI= |
| +amadan.net/oficina/home/connect v0.0.0-20260930200947-7a0f19c3a8a7/go.mod h1:YroavXe43Wietec+po8mtSk4a9Bir2h31Q3iAf4MWxI= |
| amadan.net/oficina/home/handoff v0.0.0-20260908102137-481d2d57f489 h1:RPKtyry/71i06o8L+gjAaUyexbmG56kO1YPs601URAM= |
| amadan.net/oficina/home/handoff v0.0.0-20260908102137-481d2d57f489/go.mod h1:YISpzFJ+iMgrM0rUL032OSzsAQbP4/nozvXLCCOcn1k= |
| amadan.net/rastrillo/rastrillo v0.26.1-0.20260908075707-29e4491631bd h1:jlFSt95M1ontOg+VePU+kIJpChtWqDQAbnmlxqCnbgc= |
diff --git a/internal/calls/home_release_test.go b/internal/calls/home_release_test.go| index dd9632e..2ec6357 100644 |
| --- a/internal/calls/home_release_test.go |
| +++ b/internal/calls/home_release_test.go |
| @@ -136,7 +136,7 @@ func TestHomeConnectionKeepsCurrentCallAndAgentRoutes(t *testing.T) { |
| if w.Code != 403 { |
| t.Fatalf("browser mutation lost CSRF protection: %d", w.Code) |
| } |
| - homeRedirect(t, f.do("POST", "/signout", "")) |
| + homeSignout(t, f.do("POST", "/signout", "")) |
| if w := f.do("GET", target, ""); w.Code != 303 { |
| t.Fatalf("room survives signout: %d", w.Code) |
| } |
diff --git a/internal/calls/home_router_test.go b/internal/calls/home_router_test.go| index 758ebc0..f306ac1 100644 |
| --- a/internal/calls/home_router_test.go |
| +++ b/internal/calls/home_router_test.go |
| @@ -95,6 +95,15 @@ func homeRedirect(t *testing.T, w *httptest.ResponseRecorder) string { |
| } |
| return w.Header().Get("Location") |
| } |
| +// homeSignout is POST /signout with Home configured: the app session |
| +// ends and the same POST is handed on, as a 307, to id.*'s /signout |
| +// (spec shared-chrome §9.5), which is what ends the id.* session too. |
| +func homeSignout(t *testing.T, w *httptest.ResponseRecorder) { |
| + t.Helper() |
| + if w.Code != 307 || w.Header().Get("Location") != homeTestIssuer+"/signout" { |
| + t.Fatalf("sign-out did not chain to id.*: %d %q", w.Code, w.Header().Get("Location")) |
| + } |
| +} |
| func (f *homeRouterFixture) local(t *testing.T) { |
| t.Helper() |
| homeRedirect(t, f.do("POST", "/signin", "address=alice%40example.test&force=1")) |
| @@ -144,7 +153,7 @@ func (f *homeRouterFixture) link(t *testing.T) { |
| t.Fatal("confirmation", next) |
| } |
| homeRedirect(t, f.do("POST", "/account/home/confirm", "")) |
| - homeRedirect(t, f.do("POST", "/signout", "")) |
| + homeSignout(t, f.do("POST", "/signout", "")) |
| start = homeRedirect(t, f.do("GET", "/signin", "")) |
| homeRedirect(t, f.exchange(t, start, "opaque-home-alice")) |
| } |
| @@ -159,7 +168,7 @@ func TestHomeLinksExistingMemberAndKeepsMeetings(t *testing.T) { |
| if err := f.app.db.Writer().QueryRow("SELECT created_by FROM meetings LIMIT 1").Scan(&before); err != nil { |
| t.Fatal(err) |
| } |
| - homeRedirect(t, f.do("POST", "/signout", "")) |
| + homeSignout(t, f.do("POST", "/signout", "")) |
| f.link(t) |
| w := f.do("GET", "/", "") |
| if w.Code != 200 || !strings.Contains(w.Body.String(), "Existing private meeting") { |
| @@ -211,7 +220,7 @@ func TestHomeEnabledPreservesInvitedGuestFlow(t *testing.T) { |
| if token == nil || sqid == nil { |
| t.Fatal("missing meeting links") |
| } |
| - homeRedirect(t, f.do("POST", "/signout", "")) |
| + homeSignout(t, f.do("POST", "/signout", "")) |
| path := homeRedirect(t, f.do("GET", "/j/"+token[1], "")) |
| if path != "/signin" { |
| t.Fatal(path) |
| @@ -274,7 +283,7 @@ func TestHomePublicJoinResolvesExistingMember(t *testing.T) { |
| func TestHomeRemovedMemberSigninIsRefusedWithoutServiceError(t *testing.T) { |
| f := newHomeRouterFixture(t) |
| f.link(t) |
| - homeRedirect(t, f.do("POST", "/signout", "")) |
| + homeSignout(t, f.do("POST", "/signout", "")) |
| f.app.cfg.allow = allowlist{} |
| start := homeRedirect(t, f.do("GET", "/signin", "")) |
| if w := f.exchange(t, start, "opaque-home-alice"); w.Code != 403 { |
diff --git a/internal/calls/native_auth_test.go b/internal/calls/native_auth_test.go| index 211204c..f8d61ed 100644 |
| --- a/internal/calls/native_auth_test.go |
| +++ b/internal/calls/native_auth_test.go |
| @@ -366,7 +366,7 @@ func TestNativeSessionReadsWhatTheAppReads(t *testing.T) { |
| if dest := homeRedirect(t, f.do("GET", "/j/"+token, "")); dest != "/m/"+m.Sqid+"/join" { |
| t.Fatal("invitation did not lead to its meeting", dest) |
| } |
| - homeRedirect(t, f.do("POST", "/signout", "")) |
| + homeSignout(t, f.do("POST", "/signout", "")) |
| // f.do dropped the cleared cookie; the app still holds its copy. |
| f.cookies = map[string]*http.Cookie{name: {Name: name, Value: value}} |
| if dest := homeRedirect(t, f.do("GET", "/settings", "")); !strings.HasPrefix(dest, signinPath) { |