oficina / home Public

connect: a guest session is not signed in for /signin's shortcut

Calls' anonymous per-meeting guests hold a session with method "guest",
so the already-signed-in check sent them to / instead of the exchange,
and / 404s for guests. Only HomeMethod, magiclink and keymail count now;
anything else starts the exchange as before (#74 review, finding 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 735d8761416326acb12a46213afe32e1c1e85b98 parent 2545ef5
2 files changed, +28 −5
  • connect/routes.go +16 −3
  • connect/routes_test.go +12 −2

CI failed — run details

CI log
amadan: .amadan/timeout: this job's budget is 15m0s (this runner allows up to 30m0s)
=== vet ===
CGO_ENABLED=0 go vet ./...
go: downloading amadan.net/carlos/platform.git/launchctx v0.0.0-20260911135504-fb577f96f7d1
go: downloading amadan.net/oficina/estilo.git v0.0.0-20260911182719-0bcb9287988d
internal/home/credits.go:8:2: amadan.net/oficina/estilo.git@v0.0.0-20260911182719-0bcb9287988d: invalid version: git fetch -f --end-of-options origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /home/paulca/.local/state/amadan/runner/repos/oficina__home/cache/go-mod/cache/vcs/bf4984e7a537af013cffcd0a654026b503a4e1566cddf740e3c37d2f6ae937e4: exit status 128:
	error: RPC failed; HTTP 429 curl 22 The requested URL returned error: 429
	fatal: expected flush after ref listing
internal/home/app.go:25:2: amadan.net/oficina/estilo.git@v0.0.0-20260911182719-0bcb9287988d: invalid version: git fetch -f --end-of-options origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /home/paulca/.local/state/amadan/runner/repos/oficina__home/cache/go-mod/cache/vcs/bf4984e7a537af013cffcd0a654026b503a4e1566cddf740e3c37d2f6ae937e4: exit status 128:
	error: RPC failed; HTTP 429 curl 22 The requested URL returned error: 429
	fatal: expected flush after ref listing
internal/operator/command_linux.go:10:2: amadan.net/carlos/platform.git/launchctx@v0.0.0-20260911135504-fb577f96f7d1: invalid version: git fetch -f --end-of-options origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /home/paulca/.local/state/amadan/runner/repos/oficina__home/cache/go-mod/cache/vcs/55cd75de7b117f7c5cd43edfd800f8d47521af42d6b939d46f385b6d34c14c9a: exit status 128:
	fatal: unable to access 'https://amadan.net/carlos/platform/': The requested URL returned error: 429
internal/operator/command_linux.go:11:2: amadan.net/carlos/platform.git/launchctx@v0.0.0-20260911135504-fb577f96f7d1: invalid version: git fetch -f --end-of-options origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /home/paulca/.local/state/amadan/runner/repos/oficina__home/cache/go-mod/cache/vcs/55cd75de7b117f7c5cd43edfd800f8d47521af42d6b939d46f385b6d34c14c9a: exit status 128:
	fatal: unable to access 'https://amadan.net/carlos/platform/': The requested URL returned error: 429
make: *** [Makefile:69: test-vet] Error 1

amadan: step vet failed: exit status 2

amadan: job failed at step vet — its output is above, not at the tail of this log
diff --git a/connect/routes.go b/connect/routes.go
index 9e95ebe..038b21f 100644
--- a/connect/routes.go
+++ b/connect/routes.go
@@ -127,14 +127,27 @@ func (h *Routes) Signin(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, handoff.StartPath+"?"+url.Values{"return_to": {path}}.Encode(), http.StatusSeeOther)
}
-// signedIn reports a live app session: exactly one session cookie, which
-// ReadSession resolves. An ambiguous pair or a revoked cookie is not one.
+// signedIn reports a live, signed-in app session: exactly one session
+// cookie, which ReadSession resolves to a person who signed in, through
+// Home (HomeMethod) or a local link (magiclink, keymail). An ambiguous
+// pair or a revoked cookie is not one, and neither is any other method:
+// Calls' anonymous per-meeting guests ("guest") are sessions but not
+// sign-ins, and sending one to the root instead of the exchange ends on a
+// 404 (the #74 review, finding 3). An unknown method falls back to the
+// exchange, which is what /signin did before this check existed.
func (h *Routes) signedIn(r *http.Request) bool {
if cookieCount(r, h.cfg.SessionCookie) != 1 {
return false
}
s, err := h.cfg.ReadSession(r)
- return err == nil && s != nil && s.Subject != ""
+ if err != nil || s == nil || s.Subject == "" {
+ return false
+ }
+ switch s.Method {
+ case HomeMethod, "magiclink", "keymail":
+ return true
+ }
+ return false
}
func (h *Routes) SignedOut(w http.ResponseWriter, r *http.Request) {
diff --git a/connect/routes_test.go b/connect/routes_test.go
index 48899d9..2c7fc26 100644
--- a/connect/routes_test.go
+++ b/connect/routes_test.go
@@ -125,10 +125,15 @@ func TestSigninWhenAlreadySignedIn(t *testing.T) {
_, n := navigationFixture(t)
locals := 0
h := &Routes{bridge: &Bridge{render: func(w http.ResponseWriter, r *http.Request, state, label string, status int) { w.WriteHeader(status) }}, navigation: n, cfg: RoutesConfig{Origin: navigationOrigin, SessionCookie: "__Host-rastrillo_session", ReadSession: func(r *http.Request) (*Session, error) {
- if c, err := r.Cookie("__Host-rastrillo_session"); err != nil || c.Value != "verified" {
+ c, err := r.Cookie("__Host-rastrillo_session")
+ if err != nil || (c.Value != "verified" && c.Value != "guest") {
return nil, nil
}
- return &Session{Subject: "pr_live", Method: HomeMethod, At: time.Now()}, nil
+ method := HomeMethod
+ if c.Value == "guest" {
+ method = "guest"
+ }
+ return &Session{Subject: "pr_live", Method: method, At: time.Now()}, nil
}}}
h.cfg.LocalSignin = func(w http.ResponseWriter, r *http.Request) { locals++; w.WriteHeader(299) }
live := &http.Cookie{Name: "__Host-rastrillo_session", Value: "verified"}
@@ -149,6 +154,11 @@ func TestSigninWhenAlreadySignedIn(t *testing.T) {
{"?return_to=%2Fd%2F42", nil, 303, handoff.StartPath + "?return_to=%2Fd%2F42"},
{"?return_to=%2Fd%2F42", []*http.Cookie{stale}, 303, handoff.StartPath + "?return_to=%2Fd%2F42"},
{"?return_to=%2Fd%2F42", []*http.Cookie{live, live}, 303, handoff.StartPath + "?return_to=%2Fd%2F42"},
+ // A Calls guest (an anonymous, per-meeting session) is not signed
+ // in for this purpose: it still starts the exchange, as before,
+ // rather than bouncing to a root that 404s for guests.
+ {"?return_to=%2Fd%2F42", []*http.Cookie{{Name: "__Host-rastrillo_session", Value: "guest"}}, 303, handoff.StartPath + "?return_to=%2Fd%2F42"},
+ {"", []*http.Cookie{{Name: "__Host-rastrillo_session", Value: "guest"}}, 303, handoff.StartPath + "?return_to=%2F"},
// Explicit local states still reach the local page.
{"?force=1", []*http.Cookie{live}, 299, ""},
{"?err=removed", []*http.Cookie{live}, 299, ""},