diff --git a/internal/oauth/authorize.go b/internal/oauth/authorize.go index b780dbe..2f06948 100644 --- a/internal/oauth/authorize.go +++ b/internal/oauth/authorize.go @@ -303,6 +303,11 @@ func (s *Server) serveRequest(w http.ResponseWriter, r *http.Request) { // about 39 bits, and finding one shows only an offer the owner must still // approve. func (s *Server) serveRequestByCode(w http.ResponseWriter, r *http.Request) { + // The box's page fetches it from the owner's browser, on whatever + // address the box has at home. Every answer is readable there: a + // browser hides a refusal it may not read, and a wrong code would look + // the same as no network. A refusal says nothing a code does not. + w.Header().Set("Access-Control-Allow-Origin", "*") if s.limited(&s.lookupLimit, s.lookupByIP, r) { w.Header().Set("Retry-After", "1") http.Error(w, "too many lookups", http.StatusTooManyRequests) @@ -318,9 +323,6 @@ func (s *Server) serveRequestByCode(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r) return } - // The box's page fetches it from the owner's browser, on whatever - // address the box has at home. - w.Header().Set("Access-Control-Allow-Origin", "*") writeJSON(w, http.StatusOK, s.view(c), false) } diff --git a/internal/oauth/oauth_test.go b/internal/oauth/oauth_test.go index fb14ce5..afabddb 100644 --- a/internal/oauth/oauth_test.go +++ b/internal/oauth/oauth_test.go @@ -896,11 +896,19 @@ func TestTheBoxPageFindsARequestByItsCode(t *testing.T) { if resp.StatusCode != http.StatusNotFound { t.Fatalf("an unknown code: %d", resp.StatusCode) } + // The box's page reads a refusal too, so it can tell a wrong code from + // no network. + if got := resp.Header.Get("Access-Control-Allow-Origin"); got != "*" { + t.Fatalf("an unknown code's answer is not readable from the box's page: %q", got) + } // Lookups are rate limited for everyone together. limited := false for range 60 { resp, _ := http.Get(g.srv.URL + "/oauth/requests?code=ZZZZZZ") if resp.StatusCode == http.StatusTooManyRequests { + if resp.Header.Get("Access-Control-Allow-Origin") != "*" { + t.Fatal("a limited lookup's answer is not readable from the box's page") + } limited = true break } @@ -920,7 +928,9 @@ func TestTheConsentPage(t *testing.T) { body, _ := io.ReadAll(resp.Body) page := string(body) for _, want := range []string{"Let Claude into your home?", "From example.com. FTW Cloud does not know this client", "127.0.0.1:8123", - "a program on this computer", "

After you approve, this window goes back to {{.RedirectHost}}.{{if .Local}} That is a program on this computer. Approve only if you started it.{{end}}

It asks to

-

{{if .Control}}Control lasts minutes at a time, within limits your box sets. {{end}}It can change nothing lasting by itself. You can remove it at any time in the FTW app.

+

{{if .Control}}Control lasts minutes at a time, within limits your box sets. {{end}}It can change nothing lasting by itself. You can remove it at any time, in the FTW app or on your box's own page.

{{.QR}}

Approve in the FTW app: scan the code with your phone, or

Open the FTW app

-

Or, on your box's own page, open Access and type

+

No app? On your box's own page, open Settings, then Access, and type

{{.Code}}