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(), "