oficina / docs Public

Clone
git clone https://amadan.net/oficina/docs

Plain git — no account needed to clone.

Download

Download this file

1package docs
2
3import (
4 "net/http"
5 "net/http/httptest"
6 "testing"
7)
8
9// TestHomeSignoutChainsToIssuer: in Home mode, Docs' sign-out ends the Docs
10// session and then hands the same POST on to {issuer}/signout, so the id.*
11// session ends too and the next "Sign in" asks again (spec shared-chrome
12// §9.5; the same chain connect.Routes.Signout gives every other app).
13// Ending only the Docs session left "Sign in" on the signed-out page
14// passing silently through the live id.* session.
15//
16// 307, not 303, so the hop stays a POST: id.* ignores a GET sign-out. The
17// browser sends "Origin: null" on the redirected POST, so id.* admits it by
18// Referer, which strict-origin-when-cross-origin makes exactly this origin.
19func TestHomeSignoutChainsToIssuer(t *testing.T) {
20 f := newRouterHomeFixture(t)
21 b := f.browser()
22 b.localSignin(t, "alice@example.com")
23 cookie := b.cookies[f.sessions.CookieName()]
24 if cookie == nil {
25 t.Fatal("local sign-in set no session")
26 }
27 rec := b.do("POST", "/signout", "")
28 if rec.Code != http.StatusTemporaryRedirect || rec.Header().Get("Location") != bridgeIssuer+"/signout" {
29 t.Fatalf("POST /signout: status %d Location %q, want 307 to %s/signout", rec.Code, rec.Header().Get("Location"), bridgeIssuer)
30 }
31 if got := rec.Header().Get("Referrer-Policy"); got != "strict-origin-when-cross-origin" {
32 t.Fatalf("Referrer-Policy %q: id.* admits the null-Origin hop by Referer", got)
33 }
34 if got := rec.Header().Get("Cache-Control"); got != "no-store" {
35 t.Fatalf("Cache-Control %q, want no-store", got)
36 }
37 if b.cookies[f.sessions.CookieName()] != nil {
38 t.Fatal("sign-out kept the Docs session cookie")
39 }
40 old := httptest.NewRequest("GET", bridgeOrigin+"/", nil)
41 old.AddCookie(cookie)
42 oldRec := httptest.NewRecorder()
43 f.app.Handler().ServeHTTP(oldRec, old)
44 if oldRec.Code != http.StatusSeeOther {
45 t.Fatalf("revoked session still served / (status %d)", oldRec.Code)
46 }
47 // id.* returns the browser to /signed-out, which must still render and
48 // must not start another exchange by itself.
49 signedOut := b.do("GET", "/signed-out", "")
50 if signedOut.Code != http.StatusOK || signedOut.Header().Get("Location") != "" {
51 t.Fatalf("GET /signed-out: status %d Location %q", signedOut.Code, signedOut.Header().Get("Location"))
52 }
53}
54
55// signoutChained asserts the Home-mode sign-out response: a 307 to the
56// issuer's /signout. The browser then returns to /signed-out.
57func signoutChained(t *testing.T, rec *httptest.ResponseRecorder) {
58 t.Helper()
59 if rec.Code != http.StatusTemporaryRedirect || rec.Header().Get("Location") != bridgeIssuer+"/signout" {
60 t.Fatalf("POST /signout: status %d Location %q, want 307 to %s/signout", rec.Code, rec.Header().Get("Location"), bridgeIssuer)
61 }
62}
63