package oauth import ( "context" "encoding/json" "net/http" "net/http/httptest" "net/url" "strings" "testing" ) /* ── Consent is required ────────────────────────────────────────────────── */ // A GET must ASK, not grant. This is the Phase 4 behaviour change, asserted // directly: before, a signed-in user's authorization was approved on sight. func TestAuthorizeRendersConsentRatherThanIssuingACode(t *testing.T) { h := newHarness(t) clientID := h.register() rec := h.authorize(authorizeParamsFor(clientID, verifier43)) if rec.Code == http.StatusFound { t.Fatalf("a GET issued a code without asking: %s", rec.Header().Get("Location")) } if rec.Code != http.StatusOK { t.Fatalf("status = %d, want 200 with a consent page", rec.Code) } if ct := rec.Header().Get("Content-Type"); !strings.HasPrefix(ct, "text/html") { t.Errorf("Content-Type = %q, want text/html", ct) } // No grant row may exist yet: rendering a question must not spend anything. var codes int if err := h.h.Pool.QueryRow(context.Background(), `SELECT count(*) FROM oauth_grants`).Scan(&codes); err != nil { t.Fatalf("count grants: %v", err) } if codes != 0 { t.Errorf("%d authorization codes exist after merely rendering consent", codes) } } // The page must tell a person what they are agreeing to, in their terms. func TestConsentPageShowsWhatIsBeingGranted(t *testing.T) { h := newHarness(t) clientID := h.register() rec, _ := h.consent(authorizeParamsFor(clientID, verifier43)) body := rec.Body.String() for name, want := range map[string]string{ "client name": "Test Client", "signed-in user": "oauth-user@example.test", "organisation": "OAuth Test", "resource": testResource, "approve control": "approve", "deny control": "deny", } { if !strings.Contains(body, want) { t.Errorf("the consent page does not show the %s (%q)", name, want) } } // A person asked to approve "krow.read" has not been asked anything. if strings.Contains(body, ScopeRead) && !strings.Contains(body, "Read workforce activity") { t.Error("the page shows a raw scope identifier without explaining it") } // krow.write must never appear on a screen for a flow that cannot grant it. if strings.Contains(body, ScopeWrite) { t.Error("the consent page mentions krow.write") } } // The client name is attacker-controlled: anyone may register a client called // ` body, _ := jsonMarshal(registrationRequest{ ClientName: payload, RedirectURIs: []string{testRedirect}, }) rec := httptest.NewRecorder() h.server.RegisterHandler().ServeHTTP(rec, httptest.NewRequest(http.MethodPost, "/oauth/register", strings.NewReader(body))) var reg registrationResponse _ = jsonUnmarshal(rec.Body.Bytes(), ®) page, _ := h.consent(authorizeParamsFor(reg.ClientID, verifier43)) if strings.Contains(page.Body.String(), "