Add token rotation
This commit is contained in:
@@ -1729,3 +1729,65 @@ func TestPassphraseSessionsAreMemoised(t *testing.T) {
|
||||
t.Error("the session was not memoised, so every request would derive again")
|
||||
}
|
||||
}
|
||||
|
||||
// Rotating a secret has to end the sessions that were using it, or rotation
|
||||
// would not actually revoke anything.
|
||||
func TestRotationEndsLiveSessions(t *testing.T) {
|
||||
h := newHarness(t, nil)
|
||||
|
||||
// A logged-in browser, and a page render proving the session works.
|
||||
session := &http.Cookie{Name: tokenCookie, Value: h.token}
|
||||
req, _ := http.NewRequest("GET", h.ts.URL+"/", nil)
|
||||
req.AddCookie(session)
|
||||
resp, err := h.ts.Client().Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw, _ := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if !strings.Contains(string(raw), ">friend<") {
|
||||
t.Fatal("setup: the session is not logged in")
|
||||
}
|
||||
|
||||
var replacement string
|
||||
if err := h.tokens.Update("friend", func(tok *auth.Token) error {
|
||||
s, err := tok.SetGenerated()
|
||||
replacement = s
|
||||
return err
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// The old cookie is now just a string.
|
||||
req, _ = http.NewRequest("GET", h.ts.URL+"/", nil)
|
||||
req.AddCookie(session)
|
||||
resp, err = h.ts.Client().Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw, _ = io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if strings.Contains(string(raw), ">friend<") {
|
||||
t.Error("a rotated-away secret still authenticates a session")
|
||||
}
|
||||
if !strings.Contains(string(raw), "no longer valid") {
|
||||
t.Error("the page does not explain that the session ended")
|
||||
}
|
||||
|
||||
// Uploading with the old secret is refused; the new one works.
|
||||
old := h.upload(t, []byte("x"), map[string]string{"Authorization": "Bearer " + h.token})
|
||||
old.Body.Close()
|
||||
if old.StatusCode != http.StatusUnauthorized {
|
||||
t.Errorf("upload with the old secret => %s, want 401", old.Status)
|
||||
}
|
||||
fresh := h.upload(t, []byte("x"), map[string]string{
|
||||
"Authorization": "Bearer " + replacement,
|
||||
"Vanity": "after-rotation",
|
||||
})
|
||||
if fresh.StatusCode != http.StatusCreated {
|
||||
t.Fatalf("upload with the rotated secret => %s", fresh.Status)
|
||||
}
|
||||
if res := decode[uploadResult](t, fresh); res.ID != "after-rotation" {
|
||||
t.Errorf("id = %q: the rotated token lost its vanity permission", res.ID)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user