Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions internal/oauth/authorize.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}

Expand Down
12 changes: 11 additions & 1 deletion internal/oauth/oauth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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", "<svg", AppLink, scopeWords[ScopeDispatchHold], "Control lasts minutes"} {
"a program on this computer", "<svg", AppLink, scopeWords[ScopeDispatchHold], "Control lasts minutes",
// An owner with no app approves and removes on the box's own page.
"open Settings, then Access, and type", "in the FTW app or on your box"} {
if !strings.Contains(page, want) {
t.Fatalf("the page lacks %q", want)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/oauth/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,14 @@ var consentTmpl = template.Must(template.New("consent").Parse(`<!doctype html>
<p>After you approve, this window goes back to <strong>{{.RedirectHost}}</strong>.{{if .Local}} <span class="warn">That is a program on this computer. Approve only if you started it.</span>{{end}}</p>
<h2>It asks to</h2>
<ul>{{range .Scopes}}<li>{{.}}</li>{{end}}</ul>
<p class="muted">{{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.</p>
<p class="muted">{{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.</p>
</div>
<div class="approve">
<div class="qr">{{.QR}}</div>
<div>
<p>Approve in the FTW app: scan the code with your phone, or</p>
<p><a class="button" href="{{.Link}}">Open the FTW app</a></p>
<p class="muted">Or, on your box's own page, open Access and type</p>
<p class="muted">No app? On your box's own page, open Settings, then Access, and type</p>
<p class="code">{{.Code}}</p>
</div>
</div>
Expand Down
Loading